home *** CD-ROM | disk | FTP | other *** search
/ Aminet 48 / Aminet 48 (2002)(GTI - Schatztruhe)[!][Apr 2002].iso / Aminet / text / edit / vim60src.lha / Vim / vim60 / src / search.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-09-15  |  98.4 KB  |  4,175 lines

  1. /* vi:set ts=8 sts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved    by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  7.  * See README.txt for an overview of the Vim source code.
  8.  */
  9. /*
  10.  * search.c: code for normal mode searching commands
  11.  */
  12.  
  13. #include "vim.h"
  14.  
  15. static void save_re_pat __ARGS((int idx, char_u *pat, int magic));
  16. #ifdef FEAT_EVAL
  17. static int first_submatch __ARGS((regmmatch_T *rp));
  18. #endif
  19. static int inmacro __ARGS((char_u *, char_u *));
  20. static int check_linecomment __ARGS((char_u *line));
  21. static int cls __ARGS((void));
  22. static int skip_chars __ARGS((int, int));
  23. #ifdef FEAT_TEXTOBJ
  24. static void back_in_line __ARGS((void));
  25. static void find_first_blank __ARGS((pos_T *));
  26. static void findsent_forward __ARGS((long count, int at_start_sent));
  27. #endif
  28. #ifdef FEAT_FIND_ID
  29. static void show_pat_in_path __ARGS((char_u *, int,
  30.                      int, int, FILE *, linenr_T *, long));
  31. #endif
  32. #ifdef FEAT_VIMINFO
  33. static void wvsp_one __ARGS((FILE *fp, int idx, char *s, int sc));
  34. #endif
  35.  
  36. static char_u *top_bot_msg = (char_u *)N_("search hit TOP, continuing at BOTTOM");
  37. static char_u *bot_top_msg = (char_u *)N_("search hit BOTTOM, continuing at TOP");
  38.  
  39. /*
  40.  * This file contains various searching-related routines. These fall into
  41.  * three groups:
  42.  * 1. string searches (for /, ?, n, and N)
  43.  * 2. character searches within a single line (for f, F, t, T, etc)
  44.  * 3. "other" kinds of searches like the '%' command, and 'word' searches.
  45.  */
  46.  
  47. /*
  48.  * String searches
  49.  *
  50.  * The string search functions are divided into two levels:
  51.  * lowest:  searchit(); uses an pos_T for starting position and found match.
  52.  * Highest: do_search(); uses curwin->w_cursor; calls searchit().
  53.  *
  54.  * The last search pattern is remembered for repeating the same search.
  55.  * This pattern is shared between the :g, :s, ? and / commands.
  56.  * This is in search_regcomp().
  57.  *
  58.  * The actual string matching is done using a heavily modified version of
  59.  * Henry Spencer's regular expression library.  See regexp.c.
  60.  */
  61.  
  62. /* The offset for a search command is store in a soff struct */
  63. /* Note: only spats[0].off is really used */
  64. struct soffset
  65. {
  66.     int        dir;        /* search direction */
  67.     int        line;        /* search has line offset */
  68.     int        end;        /* search set cursor at end */
  69.     long    off;        /* line or char offset */
  70. };
  71.  
  72. /* A search pattern and its attributes are stored in a spat struct */
  73. struct spat
  74. {
  75.     char_u        *pat;    /* the pattern (in allocated memory) or NULL */
  76.     int            magic;    /* magicness of the pattern */
  77.     int            no_scs;    /* no smarcase for this pattern */
  78.     struct soffset  off;
  79. };
  80.  
  81. /*
  82.  * Two search patterns are remembered: One for the :substitute command and
  83.  * one for other searches.  last_idx points to the one that was used the last
  84.  * time.
  85.  */
  86. static struct spat spats[2] =
  87. {
  88.     {NULL, TRUE, FALSE, {'/', 0, 0, 0L}},    /* last used search pat */
  89.     {NULL, TRUE, FALSE, {'/', 0, 0, 0L}}    /* last used substitute pat */
  90. };
  91.  
  92. static int last_idx = 0;    /* index in spats[] for RE_LAST */
  93.  
  94. #if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO)
  95. /* copy of spats[], for keeping the search patterns while executing autocmds */
  96. static struct spat  saved_spats[2];
  97. static int        saved_last_idx = 0;
  98. # ifdef FEAT_SEARCH_EXTRA
  99. static int        saved_no_hlsearch = 0;
  100. # endif
  101. #endif
  102.  
  103. static char_u        *mr_pattern = NULL;    /* pattern used by search_regcomp() */
  104.  
  105. #ifdef FEAT_FIND_ID
  106. /*
  107.  * Type used by find_pattern_in_path() to remember which included files have
  108.  * been searched already.
  109.  */
  110. typedef struct SearchedFile
  111. {
  112.     FILE    *fp;        /* File pointer */
  113.     char_u    *name;        /* Full name of file */
  114.     linenr_T    lnum;        /* Line we were up to in file */
  115.     int        matched;    /* Found a match in this file */
  116. } SearchedFile;
  117. #endif
  118.  
  119. /*
  120.  * translate search pattern for vim_regcomp()
  121.  *
  122.  * pat_save == RE_SEARCH: save pat in spats[RE_SEARCH].pat (normal search cmd)
  123.  * pat_save == RE_SUBST: save pat in spats[RE_SUBST].pat (:substitute command)
  124.  * pat_save == RE_BOTH: save pat in both patterns (:global command)
  125.  * pat_use  == RE_SEARCH: use previous search pattern if "pat" is NULL
  126.  * pat_use  == RE_SUBST: use previous sustitute pattern if "pat" is NULL
  127.  * pat_use  == RE_LAST: use last used pattern if "pat" is NULL
  128.  * options & SEARCH_HIS: put search string in history
  129.  * options & SEARCH_KEEP: keep previous search pattern
  130.  *
  131.  * returns FAIL if failed, OK otherwise.
  132.  */
  133.     int
  134. search_regcomp(pat, pat_save, pat_use, options, regmatch)
  135.     char_u    *pat;
  136.     int        pat_save;
  137.     int        pat_use;
  138.     int        options;
  139.     regmmatch_T    *regmatch;    /* return: pattern and ignore-case flag */
  140. {
  141.     int        magic;
  142.     int        i;
  143.  
  144.     rc_did_emsg = FALSE;
  145.     magic = p_magic;
  146.  
  147.     /*
  148.      * If no pattern given, use a previously defined pattern.
  149.      */
  150.     if (pat == NULL || *pat == NUL)
  151.     {
  152.     if (pat_use == RE_LAST)
  153.         i = last_idx;
  154.     else
  155.         i = pat_use;
  156.     if (spats[i].pat == NULL)    /* pattern was never defined */
  157.     {
  158.         if (pat_use == RE_SUBST)
  159.         EMSG(_(e_nopresub));
  160.         else
  161.         EMSG(_(e_noprevre));
  162.         rc_did_emsg = TRUE;
  163.         return FAIL;
  164.     }
  165.     pat = spats[i].pat;
  166.     magic = spats[i].magic;
  167.     no_smartcase = spats[i].no_scs;
  168.     }
  169. #ifdef FEAT_CMDHIST
  170.     else if (options & SEARCH_HIS)    /* put new pattern in history */
  171.     add_to_history(HIST_SEARCH, pat, TRUE);
  172. #endif
  173.  
  174.     mr_pattern = pat;
  175.  
  176.     /*
  177.      * Save the currently used pattern in the appropriate place,
  178.      * unless the pattern should not be remembered.
  179.      */
  180.     if (!(options & SEARCH_KEEP))
  181.     {
  182.     /* search or global command */
  183.     if (pat_save == RE_SEARCH || pat_save == RE_BOTH)
  184.         save_re_pat(RE_SEARCH, pat, magic);
  185.     /* substitute or global command */
  186.     if (pat_save == RE_SUBST || pat_save == RE_BOTH)
  187.         save_re_pat(RE_SUBST, pat, magic);
  188.     }
  189.  
  190.     regmatch->rmm_ic = ignorecase(pat);
  191.     regmatch->regprog = vim_regcomp(pat, magic);
  192.     if (regmatch->regprog == NULL)
  193.     return FAIL;
  194.     return OK;
  195. }
  196.  
  197. /*
  198.  * Get search pattern used by search_regcomp().
  199.  */
  200.     char_u *
  201. get_search_pat()
  202. {
  203.     return mr_pattern;
  204. }
  205.  
  206.     static void
  207. save_re_pat(idx, pat, magic)
  208.     int        idx;
  209.     char_u    *pat;
  210.     int        magic;
  211. {
  212.     if (spats[idx].pat != pat)
  213.     {
  214.     vim_free(spats[idx].pat);
  215.     spats[idx].pat = vim_strsave(pat);
  216.     spats[idx].magic = magic;
  217.     spats[idx].no_scs = no_smartcase;
  218.     last_idx = idx;
  219. #ifdef FEAT_SEARCH_EXTRA
  220.     /* If 'hlsearch' set and search pat changed: need redraw. */
  221.     if (p_hls)
  222.         redraw_all_later(NOT_VALID);
  223.     no_hlsearch = FALSE;
  224. #endif
  225.     }
  226. }
  227.  
  228. #if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO)
  229. /*
  230.  * Save the search patterns, so they can be restored later.
  231.  * Used before/after executing autocommands and user functions.
  232.  */
  233. static int save_level = 0;
  234.  
  235.     void
  236. save_search_patterns()
  237. {
  238.     if (save_level++ == 0)
  239.     {
  240.     saved_spats[0] = spats[0];
  241.     if (spats[0].pat != NULL)
  242.         saved_spats[0].pat = vim_strsave(spats[0].pat);
  243.     saved_spats[1] = spats[1];
  244.     if (spats[1].pat != NULL)
  245.         saved_spats[1].pat = vim_strsave(spats[1].pat);
  246.     saved_last_idx = last_idx;
  247. # ifdef FEAT_SEARCH_EXTRA
  248.     saved_no_hlsearch = no_hlsearch;
  249. # endif
  250.     }
  251. }
  252.  
  253.     void
  254. restore_search_patterns()
  255. {
  256.     if (--save_level == 0)
  257.     {
  258.     vim_free(spats[0].pat);
  259.     spats[0] = saved_spats[0];
  260.     vim_free(spats[1].pat);
  261.     spats[1] = saved_spats[1];
  262.     last_idx = saved_last_idx;
  263. # ifdef FEAT_SEARCH_EXTRA
  264.     no_hlsearch = saved_no_hlsearch;
  265. # endif
  266.     }
  267. }
  268. #endif
  269.  
  270. /*
  271.  * Return TRUE when case should be ignored for search pattern "pat".
  272.  * Uses the 'ignorecase' and 'smartcase' options.
  273.  */
  274.     int
  275. ignorecase(pat)
  276.     char_u    *pat;
  277. {
  278.     char_u    *p;
  279.     int        ic;
  280.  
  281.     ic = p_ic;
  282.     if (ic && !no_smartcase && p_scs
  283. #ifdef FEAT_INS_EXPAND
  284.                 && !(ctrl_x_mode && curbuf->b_p_inf)
  285. #endif
  286.                                     )
  287.     {
  288.     /* don't ignore case if pattern has uppercase */
  289.     for (p = pat; *p; )
  290.     {
  291. #ifdef FEAT_MBYTE
  292.         int        l;
  293.  
  294.         if (has_mbyte && (l = (*mb_ptr2len_check)(p)) > 1)
  295.         {
  296.         if (enc_utf8 && utf_isupper(utf_ptr2char(p)))
  297.         {
  298.             ic = FALSE;
  299.             break;
  300.         }
  301.         p += l;
  302.         }
  303.         else
  304. #endif
  305.         if (*p == '\\' && p[1] != NUL)    /* skip "\S" et al. */
  306.             p += 2;
  307.         else if (isupper(*p++))
  308.         {
  309.             ic = FALSE;
  310.             break;
  311.         }
  312.     }
  313.     }
  314.     no_smartcase = FALSE;
  315.  
  316.     return ic;
  317. }
  318.  
  319.     char_u *
  320. last_search_pat()
  321. {
  322.     return spats[last_idx].pat;
  323. }
  324.  
  325. #if defined(FEAT_EVAL) || defined(FEAT_VIMINFO)
  326. /*
  327.  * Set the last search pattern.  For ":let @/ =" and viminfo.
  328.  * Also set the saved search pattern, so that this works in an autocommand.
  329.  */
  330.     void
  331. set_last_search_pat(s, idx, magic, setlast)
  332.     char_u    *s;
  333.     int        idx;
  334.     int        magic;
  335.     int        setlast;
  336. {
  337.     vim_free(spats[idx].pat);
  338.     /* An empty string means that nothing should be matched. */
  339.     if (*s == NUL)
  340.     spats[idx].pat = NULL;
  341.     else
  342.     spats[idx].pat = vim_strsave(s);
  343.     spats[idx].magic = magic;
  344.     spats[idx].no_scs = FALSE;
  345.     spats[idx].off.dir = '/';
  346.     spats[idx].off.line = FALSE;
  347.     spats[idx].off.end = FALSE;
  348.     spats[idx].off.off = 0;
  349.     if (setlast)
  350.     last_idx = idx;
  351.     if (save_level)
  352.     {
  353.     vim_free(saved_spats[idx].pat);
  354.     saved_spats[idx] = spats[0];
  355.     if (spats[idx].pat == NULL)
  356.         saved_spats[idx].pat = NULL;
  357.     else
  358.         saved_spats[idx].pat = vim_strsave(spats[idx].pat);
  359.     saved_last_idx = last_idx;
  360.     }
  361. # ifdef FEAT_SEARCH_EXTRA
  362.     /* If 'hlsearch' set and search pat changed: need redraw. */
  363.     if (p_hls && idx == last_idx && !no_hlsearch)
  364.     redraw_all_later(NOT_VALID);
  365. # endif
  366. }
  367. #endif
  368.  
  369. #ifdef FEAT_SEARCH_EXTRA
  370. /*
  371.  * Get a regexp program for the last used search pattern.
  372.  * This is used for highlighting all matches in a window.
  373.  * Values returned in regmatch->regprog and regmatch->rmm_ic.
  374.  */
  375.     void
  376. last_pat_prog(regmatch)
  377.     regmmatch_T    *regmatch;
  378. {
  379.     if (spats[last_idx].pat == NULL)
  380.     {
  381.     regmatch->regprog = NULL;
  382.     return;
  383.     }
  384.     ++emsg_off;        /* So it doesn't beep if bad expr */
  385.     (void)search_regcomp((char_u *)"", 0, last_idx, SEARCH_KEEP, regmatch);
  386.     --emsg_off;
  387. }
  388. #endif
  389.  
  390. /*
  391.  * lowest level search function.
  392.  * Search for 'count'th occurrence of 'str' in direction 'dir'.
  393.  * Start at position 'pos' and return the found position in 'pos'.
  394.  *
  395.  * if (options & SEARCH_MSG) == 0 don't give any messages
  396.  * if (options & SEARCH_MSG) == SEARCH_NFMSG don't give 'notfound' messages
  397.  * if (options & SEARCH_MSG) == SEARCH_MSG give all messages
  398.  * if (options & SEARCH_HIS) put search pattern in history
  399.  * if (options & SEARCH_END) return position at end of match
  400.  * if (options & SEARCH_START) accept match at pos itself
  401.  * if (options & SEARCH_KEEP) keep previous search pattern
  402.  * if (options & SEARCH_FOLD) match only once in a closed fold
  403.  *
  404.  * Return FAIL (zero) for failure, non-zero for success.
  405.  * When FEAT_EVAL is defined, returns the index of the first matching
  406.  * subpattern plus one; one if there was none.
  407.  */
  408.     int
  409. searchit(win, buf, pos, dir, str, count, options, pat_use)
  410.     win_T    *win;        /* window to search in; can be NULL for a
  411.                    buffer without a window! */
  412.     buf_T    *buf;
  413.     pos_T    *pos;
  414.     int        dir;
  415.     char_u    *str;
  416.     long    count;
  417.     int        options;
  418.     int        pat_use;
  419. {
  420.     int        found;
  421.     linenr_T    lnum;        /* no init to shut up Apollo cc */
  422.     regmmatch_T    regmatch;
  423.     char_u    *ptr;
  424.     colnr_T    matchcol;
  425.     colnr_T    startcol;
  426.     lpos_T    endpos;
  427.     int        loop;
  428.     pos_T    start_pos;
  429.     int        at_first_line;
  430.     int        extra_col;
  431.     int        match_ok;
  432.     long    nmatched;
  433.     int        submatch = 0;
  434.     linenr_T    first_lnum;
  435.  
  436.     if (search_regcomp(str, RE_SEARCH, pat_use,
  437.            (options & (SEARCH_HIS + SEARCH_KEEP)), ®match) == FAIL)
  438.     {
  439.     if ((options & SEARCH_MSG) && !rc_did_emsg)
  440.         EMSG2(_("E383: Invalid search string: %s"), mr_pattern);
  441.     return FAIL;
  442.     }
  443.  
  444.     if (options & SEARCH_START)
  445.     extra_col = 0;
  446. #ifdef FEAT_MBYTE
  447.     /* Watch out for the "col" being MAXCOL - 2, used in a closed fold. */
  448.     else if (has_mbyte && pos->lnum >= 1 && pos->lnum <= buf->b_ml.ml_line_count
  449.                              && pos->col < MAXCOL - 2)
  450.     extra_col = (*mb_ptr2len_check)(ml_get_buf(buf, pos->lnum, FALSE)
  451.                                   + pos->col);
  452. #endif
  453.     else
  454.     extra_col = 1;
  455.  
  456. /*
  457.  * find the string
  458.  */
  459.     do    /* loop for count */
  460.     {
  461.     start_pos = *pos;    /* remember start pos for detecting no match */
  462.     found = 0;        /* default: not found */
  463.     at_first_line = TRUE;    /* default: start in first line */
  464.     if (pos->lnum == 0)    /* correct lnum for when starting in line 0 */
  465.     {
  466.         pos->lnum = 1;
  467.         pos->col = 0;
  468.         at_first_line = FALSE;  /* not in first line now */
  469.     }
  470.  
  471.     /*
  472.      * Start searching in current line, unless searching backwards and
  473.      * we're in column 0.
  474.      */
  475.     if (dir == BACKWARD && start_pos.col == 0)
  476.     {
  477.         lnum = pos->lnum - 1;
  478.         at_first_line = FALSE;
  479.     }
  480.     else
  481.         lnum = pos->lnum;
  482.  
  483.     for (loop = 0; loop <= 1; ++loop)   /* loop twice if 'wrapscan' set */
  484.     {
  485.         for ( ; lnum > 0 && lnum <= buf->b_ml.ml_line_count;
  486.                        lnum += dir, at_first_line = FALSE)
  487.         {
  488.         /*
  489.          * Look for a match somewhere in the line.
  490.          */
  491.         first_lnum = lnum;
  492.         nmatched = vim_regexec_multi(®match, win, buf,
  493.                                 lnum, (colnr_T)0);
  494.         if (nmatched > 0)
  495.         {
  496.             /* match may actually be in another line when using \zs */
  497.             lnum += regmatch.startpos[0].lnum;
  498.             ptr = ml_get_buf(buf, lnum, FALSE);
  499.             startcol = regmatch.startpos[0].col;
  500.             endpos = regmatch.endpos[0];
  501. # ifdef FEAT_EVAL
  502.             submatch = first_submatch(®match);
  503. # endif
  504.  
  505.             /*
  506.              * Forward search in the first line: match should be after
  507.              * the start position. If not, continue at the end of the
  508.              * match (this is vi compatible) or on the next char.
  509.              */
  510.             if (dir == FORWARD && at_first_line)
  511.             {
  512.             match_ok = TRUE;
  513.             /*
  514.              * When match lands on a NUL the cursor will be put
  515.              * one back afterwards, compare with that position,
  516.              * otherwise "/$" will get stuck on end of line.
  517.              */
  518.             while ((options & SEARCH_END)
  519.                 ?  (nmatched == 1
  520.                     && (int)endpos.col - 1
  521.                          < (int)start_pos.col + extra_col)
  522.                 : ((int)startcol - (ptr[startcol] == NUL)
  523.                         < (int)start_pos.col + extra_col))
  524.             {
  525.                 /*
  526.                  * If vi-compatible searching, continue at the end
  527.                  * of the match, otherwise continue one position
  528.                  * forward.
  529.                  */
  530.                 if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
  531.                 {
  532.                 if (nmatched > 1)
  533.                 {
  534.                     /* end is in next line, thus no match in
  535.                      * this line */
  536.                     match_ok = FALSE;
  537.                     break;
  538.                 }
  539.                 matchcol = endpos.col;
  540.                 /* for empty match: advance one char */
  541.                 if (matchcol == startcol
  542.                               && ptr[matchcol] != NUL)
  543.                 {
  544. #ifdef FEAT_MBYTE
  545.                     if (has_mbyte)
  546.                     matchcol +=
  547.                       (*mb_ptr2len_check)(ptr + matchcol);
  548.                     else
  549. #endif
  550.                     ++matchcol;
  551.                 }
  552.                 }
  553.                 else
  554.                 {
  555.                 matchcol = startcol;
  556.                 if (ptr[matchcol] != NUL)
  557.                 {
  558. #ifdef FEAT_MBYTE
  559.                     if (has_mbyte)
  560.                     matchcol += (*mb_ptr2len_check)(ptr
  561.                                   + matchcol);
  562.                     else
  563. #endif
  564.                     ++matchcol;
  565.                 }
  566.                 }
  567.                 if (ptr[matchcol] == NUL
  568.                     || (nmatched = vim_regexec_multi(®match,
  569.                           win, buf, lnum, matchcol)) == 0)
  570.                 {
  571.                 match_ok = FALSE;
  572.                 break;
  573.                 }
  574.                 startcol = regmatch.startpos[0].col;
  575.                 endpos = regmatch.endpos[0];
  576. # ifdef FEAT_EVAL
  577.                 submatch = first_submatch(®match);
  578. # endif
  579.  
  580.                 /* Need to get the line pointer again, a
  581.                  * multi-line search may have made it invalid. */
  582.                 ptr = ml_get_buf(buf, lnum, FALSE);
  583.             }
  584.             if (!match_ok)
  585.                 continue;
  586.             }
  587.             if (dir == BACKWARD)
  588.             {
  589.             /*
  590.              * Now, if there are multiple matches on this line,
  591.              * we have to get the last one. Or the last one before
  592.              * the cursor, if we're on that line.
  593.              * When putting the new cursor at the end, compare
  594.              * relative to the end of the match.
  595.              */
  596.             match_ok = FALSE;
  597.             for (;;)
  598.             {
  599.                 if (!at_first_line
  600.                     || ((options & SEARCH_END)
  601.                     ?  (nmatched == 1
  602.                         && (int)regmatch.endpos[0].col - 1
  603.                                    + extra_col
  604.                             <= (int)start_pos.col)
  605.                     : ((int)regmatch.startpos[0].col
  606.                                    + extra_col
  607.                               <= (int)start_pos.col)))
  608.                 {
  609.                 /* Remember this position, we use it if it's
  610.                  * the last match in the line. */
  611.                 match_ok = TRUE;
  612.                 startcol = regmatch.startpos[0].col;
  613.                 endpos = regmatch.endpos[0];
  614. # ifdef FEAT_EVAL
  615.                 submatch = first_submatch(®match);
  616. # endif
  617.                 }
  618.                 else
  619.                 break;
  620.  
  621.                 /*
  622.                  * We found a valid match, now check if there is
  623.                  * another one after it.
  624.                  * If vi-compatible searching, continue at the end
  625.                  * of the match, otherwise continue one position
  626.                  * forward.
  627.                  */
  628.                 if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
  629.                 {
  630.                 if (nmatched > 1)
  631.                     break;
  632.                 matchcol = endpos.col;
  633.                 /* for empty match: advance one char */
  634.                 if (matchcol == startcol
  635.                               && ptr[matchcol] != NUL)
  636.                 {
  637. #ifdef FEAT_MBYTE
  638.                     if (has_mbyte)
  639.                     matchcol +=
  640.                       (*mb_ptr2len_check)(ptr + matchcol);
  641.                     else
  642. #endif
  643.                     ++matchcol;
  644.                 }
  645.                 }
  646.                 else
  647.                 {
  648.                 matchcol = startcol;
  649.                 if (ptr[matchcol] != NUL)
  650.                 {
  651. #ifdef FEAT_MBYTE
  652.                     if (has_mbyte)
  653.                     matchcol +=
  654.                       (*mb_ptr2len_check)(ptr + matchcol);
  655.                     else
  656. #endif
  657.                     ++matchcol;
  658.                 }
  659.                 }
  660.                 if (ptr[matchcol] == NUL
  661.                     || (nmatched = vim_regexec_multi(®match,
  662.                           win, buf, lnum, matchcol)) == 0)
  663.                 break;
  664.  
  665.                 /* Need to get the line pointer again, a
  666.                  * multi-line search may have made it invalid. */
  667.                 ptr = ml_get_buf(buf, lnum, FALSE);
  668.             }
  669.  
  670.             /*
  671.              * If there is only a match after the cursor, skip
  672.              * this match.
  673.              */
  674.             if (!match_ok)
  675.                 continue;
  676.             }
  677.  
  678.             if (options & SEARCH_END && !(options & SEARCH_NOOF))
  679.             {
  680.             pos->lnum = endpos.lnum + first_lnum;
  681.             pos->col = endpos.col - 1;
  682.             }
  683.             else
  684.             {
  685.             pos->lnum = lnum;
  686.             pos->col = startcol;
  687.             }
  688.             found = 1;
  689.  
  690.             /* Set variables used for 'incsearch' highlighting. */
  691.             search_match_lines = endpos.lnum - (lnum - first_lnum);
  692.             search_match_endcol = endpos.col;
  693.             break;
  694.         }
  695.         line_breakcheck();    /* stop if ctrl-C typed */
  696.         if (got_int)
  697.             break;
  698.  
  699.         if (loop && lnum == start_pos.lnum)
  700.             break;        /* if second loop, stop where started */
  701.         }
  702.         at_first_line = FALSE;
  703.  
  704.         /*
  705.          * stop the search if wrapscan isn't set, after an interrupt and
  706.          * after a match
  707.          */
  708.         if (!p_ws || got_int || found)
  709.         break;
  710.  
  711.         /*
  712.          * If 'wrapscan' is set we continue at the other end of the file.
  713.          * If 'shortmess' does not contain 's', we give a message.
  714.          * This message is also remembered in keep_msg for when the screen
  715.          * is redrawn. The keep_msg is cleared whenever another message is
  716.          * written.
  717.          */
  718.         if (dir == BACKWARD)    /* start second loop at the other end */
  719.         {
  720.         lnum = buf->b_ml.ml_line_count;
  721.         if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
  722.             give_warning((char_u *)_(top_bot_msg), TRUE);
  723.         }
  724.         else
  725.         {
  726.         lnum = 1;
  727.         if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
  728.             give_warning((char_u *)_(bot_top_msg), TRUE);
  729.         }
  730.     }
  731.     if (got_int)
  732.         break;
  733.     }
  734.     while (--count > 0 && found);   /* stop after count matches or no match */
  735.  
  736.     vim_free(regmatch.regprog);
  737.  
  738.     if (!found)            /* did not find it */
  739.     {
  740.     if (got_int)
  741.         EMSG(_(e_interr));
  742.     else if ((options & SEARCH_MSG) == SEARCH_MSG)
  743.     {
  744.         if (p_ws)
  745.         EMSG2(_(e_patnotf2), mr_pattern);
  746.         else if (lnum == 0)
  747.         EMSG2(_("E384: search hit TOP without match for: %s"), mr_pattern);
  748.         else
  749.         EMSG2(_("E385: search hit BOTTOM without match for: %s"), mr_pattern);
  750.     }
  751.     return FAIL;
  752.     }
  753.  
  754.     return submatch + 1;
  755. }
  756.  
  757. #ifdef FEAT_EVAL
  758. /*
  759.  * Return the number of the first subpat that matched.
  760.  */
  761.     static int
  762. first_submatch(rp)
  763.     regmmatch_T    *rp;
  764. {
  765.     int        submatch;
  766.  
  767.     for (submatch = 1; ; ++submatch)
  768.     {
  769.     if (rp->startpos[submatch].lnum >= 0)
  770.         break;
  771.     if (submatch == 9)
  772.     {
  773.         submatch = 0;
  774.         break;
  775.     }
  776.     }
  777.     return submatch;
  778. }
  779. #endif
  780.  
  781. /*
  782.  * Highest level string search function.
  783.  * Search for the 'count'th occurence of string 'str' in direction 'dirc'
  784.  *          If 'dirc' is 0: use previous dir.
  785.  *    If 'str' is NULL or empty : use previous string.
  786.  *    If 'options & SEARCH_REV' : go in reverse of previous dir.
  787.  *    If 'options & SEARCH_ECHO': echo the search command and handle options
  788.  *    If 'options & SEARCH_MSG' : may give error message
  789.  *    If 'options & SEARCH_OPT' : interpret optional flags
  790.  *    If 'options & SEARCH_HIS' : put search pattern in history
  791.  *    If 'options & SEARCH_NOOF': don't add offset to position
  792.  *    If 'options & SEARCH_MARK': set previous context mark
  793.  *    If 'options & SEARCH_KEEP': keep previous search pattern
  794.  *    If 'options & SEARCH_START': accept match at curpos itself
  795.  *
  796.  * Careful: If spats[0].off.line == TRUE and spats[0].off.off == 0 this
  797.  * makes the movement linewise without moving the match position.
  798.  *
  799.  * return 0 for failure, 1 for found, 2 for found and line offset added
  800.  */
  801.     int
  802. do_search(oap, dirc, str, count, options)
  803.     oparg_T        *oap;
  804.     int            dirc;
  805.     char_u       *str;
  806.     long        count;
  807.     int            options;
  808. {
  809.     pos_T        pos;    /* position of the last match */
  810.     char_u        *searchstr;
  811.     struct soffset  old_off;
  812.     int            retval;    /* Return value */
  813.     char_u        *p;
  814.     long        c;
  815.     char_u        *dircp;
  816.  
  817.     /*
  818.      * A line offset is not remembered, this is vi compatible.
  819.      */
  820.     if (spats[0].off.line && vim_strchr(p_cpo, CPO_LINEOFF) != NULL)
  821.     {
  822.     spats[0].off.line = FALSE;
  823.     spats[0].off.off = 0;
  824.     }
  825.  
  826.     /*
  827.      * Save the values for when (options & SEARCH_KEEP) is used.
  828.      * (there is no "if ()" around this because gcc wants them initialized)
  829.      */
  830.     old_off = spats[0].off;
  831.  
  832.     pos = curwin->w_cursor;    /* start searching at the cursor position */
  833.  
  834.     /*
  835.      * Find out the direction of the search.
  836.      */
  837.     if (dirc == 0)
  838.     dirc = spats[0].off.dir;
  839.     else
  840.     spats[0].off.dir = dirc;
  841.     if (options & SEARCH_REV)
  842.     {
  843. #ifdef WIN32
  844.     /* There is a bug in the Visual C++ 2.2 compiler which means that
  845.      * dirc always ends up being '/' */
  846.     dirc = (dirc == '/')  ?  '?'  :  '/';
  847. #else
  848.     if (dirc == '/')
  849.         dirc = '?';
  850.     else
  851.         dirc = '/';
  852. #endif
  853.     }
  854.  
  855. #ifdef FEAT_FOLDING
  856.     /* If the cursor is in a closed fold, don't find another match in the same
  857.      * fold. */
  858.     if (dirc == '/')
  859.     {
  860.     if (hasFolding(pos.lnum, NULL, &pos.lnum))
  861.         pos.col = MAXCOL - 2;    /* avoid overflow when adding 1 */
  862.     }
  863.     else
  864.     {
  865.     if (hasFolding(pos.lnum, &pos.lnum, NULL))
  866.         pos.col = 0;
  867.     }
  868. #endif
  869.  
  870. #ifdef FEAT_SEARCH_EXTRA
  871.     /*
  872.      * Turn 'hlsearch' highlighting back on.
  873.      */
  874.     if (no_hlsearch && !(options & SEARCH_KEEP))
  875.     {
  876.     redraw_all_later(NOT_VALID);
  877.     no_hlsearch = FALSE;
  878.     }
  879. #endif
  880.  
  881.     /*
  882.      * Repeat the search when pattern followed by ';', e.g. "/foo/;?bar".
  883.      */
  884.     for (;;)
  885.     {
  886.     searchstr = str;
  887.     dircp = NULL;
  888.                         /* use previous pattern */
  889.     if (str == NULL || *str == NUL || *str == dirc)
  890.     {
  891.         if (spats[RE_SEARCH].pat == NULL)        /* no previous pattern */
  892.         {
  893.         EMSG(_(e_noprevre));
  894.         retval = 0;
  895.         goto end_do_search;
  896.         }
  897.         /* make search_regcomp() use spats[RE_SEARCH].pat */
  898.         searchstr = (char_u *)"";
  899.     }
  900.  
  901.     if (str != NULL && *str != NUL)    /* look for (new) offset */
  902.     {
  903.         /*
  904.          * Find end of regular expression.
  905.          * If there is a matching '/' or '?', toss it.
  906.          */
  907.         p = skip_regexp(str, dirc, (int)p_magic);
  908.         if (*p == dirc)
  909.         {
  910.         dircp = p;    /* remember where we put the NUL */
  911.         *p++ = NUL;
  912.         }
  913.         spats[0].off.line = FALSE;
  914.         spats[0].off.end = FALSE;
  915.         spats[0].off.off = 0;
  916.         /*
  917.          * Check for a line offset or a character offset.
  918.          * For get_address (echo off) we don't check for a character
  919.          * offset, because it is meaningless and the 's' could be a
  920.          * substitute command.
  921.          */
  922.         if (*p == '+' || *p == '-' || isdigit(*p))
  923.         spats[0].off.line = TRUE;
  924.         else if ((options & SEARCH_OPT) &&
  925.                     (*p == 'e' || *p == 's' || *p == 'b'))
  926.         {
  927.         if (*p == 'e')        /* end */
  928.             spats[0].off.end = SEARCH_END;
  929.         ++p;
  930.         }
  931.         if (isdigit(*p) || *p == '+' || *p == '-')       /* got an offset */
  932.         {
  933.                         /* 'nr' or '+nr' or '-nr' */
  934.         if (isdigit(*p) || isdigit(*(p + 1)))
  935.             spats[0].off.off = atol((char *)p);
  936.         else if (*p == '-')        /* single '-' */
  937.             spats[0].off.off = -1;
  938.         else                /* single '+' */
  939.             spats[0].off.off = 1;
  940.         ++p;
  941.         while (isdigit(*p))        /* skip number */
  942.             ++p;
  943.         }
  944.         searchcmdlen = (int)(p - str); /* compute length of search command
  945.                                 for get_address() */
  946.         str = p;                /* put str after search command */
  947.     }
  948.  
  949.     if ((options & SEARCH_ECHO) && messaging() && !cmd_silent)
  950.     {
  951.         char_u    *msgbuf;
  952.         char_u    *trunc;
  953.  
  954.         if (*searchstr == NUL)
  955.         p = spats[last_idx].pat;
  956.         else
  957.         p = searchstr;
  958.         msgbuf = alloc((unsigned)(STRLEN(p) + 40));
  959.         if (msgbuf != NULL)
  960.         {
  961.         msgbuf[0] = dirc;
  962.         STRCPY(msgbuf + 1, p);
  963.         if (spats[0].off.line || spats[0].off.end || spats[0].off.off)
  964.         {
  965.             p = msgbuf + STRLEN(msgbuf);
  966.             *p++ = dirc;
  967.             if (spats[0].off.end)
  968.             *p++ = 'e';
  969.             else if (!spats[0].off.line)
  970.             *p++ = 's';
  971.             if (spats[0].off.off > 0 || spats[0].off.line)
  972.             *p++ = '+';
  973.             if (spats[0].off.off != 0 || spats[0].off.line)
  974.             sprintf((char *)p, "%ld", spats[0].off.off);
  975.             else
  976.             *p = NUL;
  977.         }
  978.  
  979.         msg_start();
  980.         trunc = msg_strtrunc(msgbuf);
  981.         if (trunc != NULL)
  982.         {
  983.             msg_outtrans(trunc);
  984.             vim_free(trunc);
  985.         }
  986.         else
  987.             msg_outtrans(msgbuf);
  988.         msg_clr_eos();
  989.         msg_check();
  990.         vim_free(msgbuf);
  991.  
  992.         gotocmdline(FALSE);
  993.         out_flush();
  994.         msg_nowait = TRUE;        /* don't wait for this message */
  995.         }
  996.     }
  997.  
  998.     /*
  999.      * If there is a character offset, subtract it from the current
  1000.      * position, so we don't get stuck at "?pat?e+2" or "/pat/s-2".
  1001.      * This is not done for a line offset, because then we would not be vi
  1002.      * compatible.
  1003.      */
  1004.     if (!spats[0].off.line && spats[0].off.off)
  1005.     {
  1006.         if (spats[0].off.off > 0)
  1007.         {
  1008.         for (c = spats[0].off.off; c; --c)
  1009.             if (decl(&pos) == -1)
  1010.             break;
  1011.         if (c)            /* at start of buffer */
  1012.         {
  1013.             pos.lnum = 0;    /* allow lnum == 0 here */
  1014.             pos.col = MAXCOL;
  1015.         }
  1016.         }
  1017.         else
  1018.         {
  1019.         for (c = spats[0].off.off; c; ++c)
  1020.             if (incl(&pos) == -1)
  1021.             break;
  1022.         if (c)            /* at end of buffer */
  1023.         {
  1024.             pos.lnum = curbuf->b_ml.ml_line_count + 1;
  1025.             pos.col = 0;
  1026.         }
  1027.         }
  1028.     }
  1029.  
  1030. #ifdef FEAT_FKMAP    /* when in Farsi mode, reverse the character flow */
  1031.     if (p_altkeymap && curwin->w_p_rl)
  1032.          lrFswap(searchstr,0);
  1033. #endif
  1034.  
  1035.     c = searchit(curwin, curbuf, &pos, dirc == '/' ? FORWARD : BACKWARD,
  1036.         searchstr, count, spats[0].off.end + (options &
  1037.                (SEARCH_KEEP + SEARCH_HIS + SEARCH_MSG + SEARCH_START +
  1038.                ((str != NULL && *str == ';') ? 0 : SEARCH_NOOF))),
  1039.         RE_LAST);
  1040.     if (dircp != NULL)
  1041.         *dircp = dirc;    /* restore second '/' or '?' for normal_cmd() */
  1042.     if (c == FAIL)
  1043.     {
  1044.         retval = 0;
  1045.         goto end_do_search;
  1046.     }
  1047.     if (spats[0].off.end && oap != NULL)
  1048.         oap->inclusive = TRUE;  /* 'e' includes last character */
  1049.  
  1050.     retval = 1;            /* pattern found */
  1051.  
  1052.     /*
  1053.      * Add character and/or line offset
  1054.      */
  1055.     if (!(options & SEARCH_NOOF) || *str == ';')
  1056.     {
  1057.         if (spats[0].off.line)    /* Add the offset to the line number. */
  1058.         {
  1059.         c = pos.lnum + spats[0].off.off;
  1060.         if (c < 1)
  1061.             pos.lnum = 1;
  1062.         else if (c > curbuf->b_ml.ml_line_count)
  1063.             pos.lnum = curbuf->b_ml.ml_line_count;
  1064.         else
  1065.             pos.lnum = c;
  1066.         pos.col = 0;
  1067.  
  1068.         retval = 2;        /* pattern found, line offset added */
  1069.         }
  1070.         else
  1071.         {
  1072.         /* to the right, check for end of file */
  1073.         if (spats[0].off.off > 0)
  1074.         {
  1075.             for (c = spats[0].off.off; c; --c)
  1076.             if (incl(&pos) == -1)
  1077.                 break;
  1078.         }
  1079.         /* to the left, check for start of file */
  1080.         else
  1081.         {
  1082.             if ((c = pos.col + spats[0].off.off) >= 0)
  1083.             pos.col = c;
  1084.             else
  1085.             for (c = spats[0].off.off; c; ++c)
  1086.                 if (decl(&pos) == -1)
  1087.                 break;
  1088.         }
  1089.         }
  1090.     }
  1091.  
  1092.     /*
  1093.      * The search command can be followed by a ';' to do another search.
  1094.      * For example: "/pat/;/foo/+3;?bar"
  1095.      * This is like doing another search command, except:
  1096.      * - The remembered direction '/' or '?' is from the first search.
  1097.      * - When an error happens the cursor isn't moved at all.
  1098.      * Don't do this when called by get_address() (it handles ';' itself).
  1099.      */
  1100.     if (!(options & SEARCH_OPT) || str == NULL || *str != ';')
  1101.         break;
  1102.  
  1103.     dirc = *++str;
  1104.     if (dirc != '?' && dirc != '/')
  1105.     {
  1106.         retval = 0;
  1107.         EMSG(_("E386: Expected '?' or '/'  after ';'"));
  1108.         goto end_do_search;
  1109.     }
  1110.     ++str;
  1111.     }
  1112.  
  1113.     if (options & SEARCH_MARK)
  1114.     setpcmark();
  1115.     curwin->w_cursor = pos;
  1116.     curwin->w_set_curswant = TRUE;
  1117.  
  1118. end_do_search:
  1119.     if (options & SEARCH_KEEP)
  1120.     spats[0].off = old_off;
  1121.     return retval;
  1122. }
  1123.  
  1124. #if defined(FEAT_INS_EXPAND) || defined(PROTO)
  1125. /*
  1126.  * search_for_exact_line(buf, pos, dir, pat)
  1127.  *
  1128.  * Search for a line starting with the given pattern (ignoring leading
  1129.  * white-space), starting from pos and going in direction dir.    pos will
  1130.  * contain the position of the match found.    Blank lines match only if
  1131.  * ADDING is set.  if p_ic is set then the pattern must be in lowercase.
  1132.  * Return OK for success, or FAIL if no line found.
  1133.  */
  1134.     int
  1135. search_for_exact_line(buf, pos, dir, pat)
  1136.     buf_T    *buf;
  1137.     pos_T    *pos;
  1138.     int        dir;
  1139.     char_u    *pat;
  1140. {
  1141.     linenr_T    start = 0;
  1142.     char_u    *ptr;
  1143.     char_u    *p;
  1144.  
  1145.     if (buf->b_ml.ml_line_count == 0)
  1146.     return FAIL;
  1147.     for (;;)
  1148.     {
  1149.     pos->lnum += dir;
  1150.     if (pos->lnum < 1)
  1151.     {
  1152.         if (p_ws)
  1153.         {
  1154.         pos->lnum = buf->b_ml.ml_line_count;
  1155.         if (!shortmess(SHM_SEARCH))
  1156.             give_warning((char_u *)_(top_bot_msg), TRUE);
  1157.         }
  1158.         else
  1159.         {
  1160.         pos->lnum = 1;
  1161.         break;
  1162.         }
  1163.     }
  1164.     else if (pos->lnum > buf->b_ml.ml_line_count)
  1165.     {
  1166.         if (p_ws)
  1167.         {
  1168.         pos->lnum = 1;
  1169.         if (!shortmess(SHM_SEARCH))
  1170.             give_warning((char_u *)_(bot_top_msg), TRUE);
  1171.         }
  1172.         else
  1173.         {
  1174.         pos->lnum = 1;
  1175.         break;
  1176.         }
  1177.     }
  1178.     if (pos->lnum == start)
  1179.         break;
  1180.     if (start == 0)
  1181.         start = pos->lnum;
  1182.     ptr = ml_get_buf(buf, pos->lnum, FALSE);
  1183.     p = skipwhite(ptr);
  1184.     pos->col = (colnr_T) (p - ptr);
  1185.  
  1186.     /* when adding lines the matching line may be empty but it is not
  1187.      * ignored because we are interested in the next line -- Acevedo */
  1188.     if ((continue_status & CONT_ADDING) && !(continue_status & CONT_SOL))
  1189.     {
  1190.         if ((p_ic ? MB_STRICMP(p, pat) : STRCMP(p, pat)) == 0)
  1191.         return OK;
  1192.     }
  1193.     else if (*p != NUL)    /* ignore empty lines */
  1194.     {    /* expanding lines or words */
  1195.         if ((p_ic ? MB_STRNICMP(p, pat, completion_length)
  1196.                    : STRNCMP(p, pat, completion_length)) == 0)
  1197.         return OK;
  1198.     }
  1199.     }
  1200.     return FAIL;
  1201. }
  1202. #endif /* FEAT_INS_EXPAND */
  1203.  
  1204. /*
  1205.  * Character Searches
  1206.  */
  1207.  
  1208. /*
  1209.  * Search for a character in a line.  If "t_cmd" is FALSE, move to the
  1210.  * position of the character, otherwise move to just before the char.
  1211.  * Do this "cap->count1" times.
  1212.  * Return FAIL or OK.
  1213.  */
  1214.     int
  1215. searchc(cap, t_cmd)
  1216.     cmdarg_T    *cap;
  1217.     int        t_cmd;
  1218. {
  1219.     int            c = cap->nchar;    /* char to search for */
  1220.     int            dir = cap->arg;    /* TRUE for searching forward */
  1221.     long        count = cap->count1;    /* repeat count */
  1222.     static int        lastc = NUL;    /* last character searched for */
  1223.     static int        lastcdir;    /* last direction of character search */
  1224.     static int        last_t_cmd;    /* last search t_cmd */
  1225.     int            col;
  1226.     char_u        *p;
  1227.     int            len;
  1228. #ifdef FEAT_MBYTE
  1229.     static char_u    bytes[MB_MAXBYTES];
  1230.     static int        bytelen = 1;    /* >1 for multi-byte char */
  1231. #endif
  1232.  
  1233.     if (c != NUL)    /* normal search: remember args for repeat */
  1234.     {
  1235.     if (!KeyStuffed)    /* don't remember when redoing */
  1236.     {
  1237.         lastc = c;
  1238.         lastcdir = dir;
  1239.         last_t_cmd = t_cmd;
  1240. #ifdef FEAT_MBYTE
  1241.         bytelen = (*mb_char2bytes)(c, bytes);
  1242.         if (cap->ncharC1 != 0)
  1243.         {
  1244.         bytelen += (*mb_char2bytes)(cap->ncharC1, bytes + bytelen);
  1245.         if (cap->ncharC2 != 0)
  1246.             bytelen += (*mb_char2bytes)(cap->ncharC2, bytes + bytelen);
  1247.         }
  1248. #endif
  1249.     }
  1250.     }
  1251.     else        /* repeat previous search */
  1252.     {
  1253.     if (lastc == NUL)
  1254.         return FAIL;
  1255.     if (dir)    /* repeat in opposite direction */
  1256.         dir = -lastcdir;
  1257.     else
  1258.         dir = lastcdir;
  1259.     t_cmd = last_t_cmd;
  1260.     c = lastc;
  1261.     /* For multi-byte re-use last bytes[] and bytelen. */
  1262.     }
  1263.  
  1264.     p = ml_get_curline();
  1265.     col = curwin->w_cursor.col;
  1266.     len = (int)STRLEN(p);
  1267.  
  1268.     while (count--)
  1269.     {
  1270. #ifdef FEAT_MBYTE
  1271.     if (has_mbyte)
  1272.     {
  1273.         for (;;)
  1274.         {
  1275.         if (dir > 0)
  1276.         {
  1277.             col += (*mb_ptr2len_check)(p + col);
  1278.             if (col >= len)
  1279.             return FAIL;
  1280.         }
  1281.         else
  1282.         {
  1283.             if (col == 0)
  1284.             return FAIL;
  1285.             col -= (*mb_head_off)(p, p + col - 1) + 1;
  1286.         }
  1287.         if (bytelen == 1)
  1288.         {
  1289.             if (p[col] == c)
  1290.             break;
  1291.         }
  1292.         else
  1293.         {
  1294.             if (vim_memcmp(p + col, bytes, bytelen) == 0)
  1295.             break;
  1296.         }
  1297.         }
  1298.     }
  1299.     else
  1300. #endif
  1301.     {
  1302.         for (;;)
  1303.         {
  1304.         if ((col += dir) < 0 || col >= len)
  1305.             return FAIL;
  1306.         if (p[col] == c)
  1307.             break;
  1308.         }
  1309.     }
  1310.     }
  1311.  
  1312.     if (t_cmd)
  1313.     {
  1314.     /* backup to before the character (possibly double-byte) */
  1315.     col -= dir;
  1316. #ifdef FEAT_MBYTE
  1317.     if (has_mbyte)
  1318.     {
  1319.         if (dir < 0)
  1320.         /* Landed on the search char which is bytelen long */
  1321.         col += bytelen - 1;
  1322.         else
  1323.         /* To previous char, which may be multi-byte. */
  1324.         col -= (*mb_head_off)(p, p + col);
  1325.     }
  1326. #endif
  1327.     }
  1328.     curwin->w_cursor.col = col;
  1329.  
  1330.     return OK;
  1331. }
  1332.  
  1333. /*
  1334.  * "Other" Searches
  1335.  */
  1336.  
  1337. /*
  1338.  * findmatch - find the matching paren or brace
  1339.  *
  1340.  * Improvement over vi: Braces inside quotes are ignored.
  1341.  */
  1342.     pos_T *
  1343. findmatch(oap, initc)
  1344.     oparg_T   *oap;
  1345.     int        initc;
  1346. {
  1347.     return findmatchlimit(oap, initc, 0, 0);
  1348. }
  1349.  
  1350. /*
  1351.  * findmatchlimit -- find the matching paren or brace, if it exists within
  1352.  * maxtravel lines of here.  A maxtravel of 0 means search until falling off
  1353.  * the edge of the file.
  1354.  *
  1355.  * "initc" is the character to find a match for.  NUL means to find the
  1356.  * character at or after the cursor.
  1357.  *
  1358.  * flags: FM_BACKWARD    search backwards (when initc is '/', '*' or '#')
  1359.  *      FM_FORWARD    search forwards (when initc is '/', '*' or '#')
  1360.  *      FM_BLOCKSTOP    stop at start/end of block ({ or } in column 0)
  1361.  *      FM_SKIPCOMM    skip comments (not implemented yet!)
  1362.  */
  1363.  
  1364.     pos_T *
  1365. findmatchlimit(oap, initc, flags, maxtravel)
  1366.     oparg_T    *oap;
  1367.     int        initc;
  1368.     int        flags;
  1369.     int        maxtravel;
  1370. {
  1371.     static pos_T pos;            /* current search position */
  1372.     int        findc = 0;        /* matching brace */
  1373.     int        c;
  1374.     int        count = 0;        /* cumulative number of braces */
  1375.     int        backwards = FALSE;    /* init for gcc */
  1376.     int        inquote = FALSE;    /* TRUE when inside quotes */
  1377.     char_u    *linep;            /* pointer to current line */
  1378.     char_u    *ptr;
  1379.     int        do_quotes;        /* check for quotes in current line */
  1380.     int        at_start;        /* do_quotes value at start position */
  1381.     int        hash_dir = 0;        /* Direction searched for # things */
  1382.     int        comment_dir = 0;    /* Direction searched for comments */
  1383.     pos_T    match_pos;        /* Where last slash-star was found */
  1384.     int        start_in_quotes;    /* start position is in quotes */
  1385.     int        traveled = 0;        /* how far we've searched so far */
  1386.     int        ignore_cend = FALSE;    /* ignore comment end */
  1387.     int        cpo_match;        /* vi compatible matching */
  1388.     int        dir;            /* Direction to search */
  1389.     int        comment_col = MAXCOL;   /* start of / / comment */
  1390.  
  1391.     pos = curwin->w_cursor;
  1392.     linep = ml_get(pos.lnum);
  1393.  
  1394.     cpo_match = (vim_strchr(p_cpo, CPO_MATCH) != NULL);
  1395.  
  1396.     /* Direction to search when initc is '/', '*' or '#' */
  1397.     if (flags & FM_BACKWARD)
  1398.     dir = BACKWARD;
  1399.     else if (flags & FM_FORWARD)
  1400.     dir = FORWARD;
  1401.     else
  1402.     dir = 0;
  1403.  
  1404.     /*
  1405.      * if initc given, look in the table for the matching character
  1406.      * '/' and '*' are special cases: look for start or end of comment.
  1407.      * When '/' is used, we ignore running backwards into an star-slash, for
  1408.      * "[*" command, we just want to find any comment.
  1409.      */
  1410.     if (initc == '/' || initc == '*')
  1411.     {
  1412.     comment_dir = dir;
  1413.     if (initc == '/')
  1414.         ignore_cend = TRUE;
  1415.     backwards = (dir == FORWARD) ? FALSE : TRUE;
  1416.     initc = NUL;
  1417.     }
  1418.     else if (initc != '#' && initc != NUL)
  1419.     {
  1420.     /* 'matchpairs' is "x:y,x:y" */
  1421.     for (ptr = curbuf->b_p_mps; *ptr; ptr += 2)
  1422.     {
  1423.         if (*ptr == initc)
  1424.         {
  1425.         findc = initc;
  1426.         initc = ptr[2];
  1427.         backwards = TRUE;
  1428.         break;
  1429.         }
  1430.         ptr += 2;
  1431.         if (*ptr == initc)
  1432.         {
  1433.         findc = initc;
  1434.         initc = ptr[-2];
  1435.         backwards = FALSE;
  1436.         break;
  1437.         }
  1438.     }
  1439.     if (!findc)        /* invalid initc! */
  1440.         return NULL;
  1441.     }
  1442.     /*
  1443.      * Either initc is '#', or no initc was given and we need to look under the
  1444.      * cursor.
  1445.      */
  1446.     else
  1447.     {
  1448.     if (initc == '#')
  1449.     {
  1450.         hash_dir = dir;
  1451.     }
  1452.     else
  1453.     {
  1454.         /*
  1455.          * initc was not given, must look for something to match under
  1456.          * or near the cursor.
  1457.          * Only check for special things when 'cpo' doesn't have '%'.
  1458.          */
  1459.         if (!cpo_match)
  1460.         {
  1461.         /* Are we before or at #if, #else etc.? */
  1462.         ptr = skipwhite(linep);
  1463.         if (*ptr == '#' && pos.col <= (colnr_T)(ptr - linep))
  1464.         {
  1465.             ptr = skipwhite(ptr + 1);
  1466.             if (   STRNCMP(ptr, "if", 2) == 0
  1467.             || STRNCMP(ptr, "endif", 5) == 0
  1468.             || STRNCMP(ptr, "el", 2) == 0)
  1469.             hash_dir = 1;
  1470.         }
  1471.  
  1472.         /* Are we on a comment? */
  1473.         else if (linep[pos.col] == '/')
  1474.         {
  1475.             if (linep[pos.col + 1] == '*')
  1476.             {
  1477.             comment_dir = FORWARD;
  1478.             backwards = FALSE;
  1479.             pos.col++;
  1480.             }
  1481.             else if (pos.col > 0 && linep[pos.col - 1] == '*')
  1482.             {
  1483.             comment_dir = BACKWARD;
  1484.             backwards = TRUE;
  1485.             pos.col--;
  1486.             }
  1487.         }
  1488.         else if (linep[pos.col] == '*')
  1489.         {
  1490.             if (linep[pos.col + 1] == '/')
  1491.             {
  1492.             comment_dir = BACKWARD;
  1493.             backwards = TRUE;
  1494.             }
  1495.             else if (pos.col > 0 && linep[pos.col - 1] == '/')
  1496.             {
  1497.             comment_dir = FORWARD;
  1498.             backwards = FALSE;
  1499.             }
  1500.         }
  1501.         }
  1502.  
  1503.         /*
  1504.          * If we are not on a comment or the # at the start of a line, then
  1505.          * look for brace anywhere on this line after the cursor.
  1506.          */
  1507.         if (!hash_dir && !comment_dir)
  1508.         {
  1509.         /*
  1510.          * Find the brace under or after the cursor.
  1511.          * If beyond the end of the line, use the last character in
  1512.          * the line.
  1513.          */
  1514.         if (linep[pos.col] == NUL && pos.col)
  1515.             --pos.col;
  1516.         for (;;)
  1517.         {
  1518.             initc = linep[pos.col];
  1519.             if (initc == NUL)
  1520.             break;
  1521.  
  1522.             for (ptr = curbuf->b_p_mps; *ptr; ++ptr)
  1523.             {
  1524.             if (*ptr == initc)
  1525.             {
  1526.                 findc = ptr[2];
  1527.                 backwards = FALSE;
  1528.                 break;
  1529.             }
  1530.             ptr += 2;
  1531.             if (*ptr == initc)
  1532.             {
  1533.                 findc = ptr[-2];
  1534.                 backwards = TRUE;
  1535.                 break;
  1536.             }
  1537.             if (!*++ptr)
  1538.                 break;
  1539.             }
  1540.             if (findc)
  1541.             break;
  1542. #ifdef FEAT_MBYTE
  1543.             if (has_mbyte)
  1544.             pos.col += (*mb_ptr2len_check)(linep + pos.col);
  1545.             else
  1546. #endif
  1547.             ++pos.col;
  1548.         }
  1549.         if (!findc)
  1550.         {
  1551.             /* no brace in the line, maybe use "  #if" then */
  1552.             if (!cpo_match && *skipwhite(linep) == '#')
  1553.             hash_dir = 1;
  1554.             else
  1555.             return NULL;
  1556.         }
  1557.         }
  1558.     }
  1559.     if (hash_dir)
  1560.     {
  1561.         /*
  1562.          * Look for matching #if, #else, #elif, or #endif
  1563.          */
  1564.         if (oap != NULL)
  1565.         oap->motion_type = MLINE;   /* Linewise for this case only */
  1566.         if (initc != '#')
  1567.         {
  1568.         ptr = skipwhite(skipwhite(linep) + 1);
  1569.         if (STRNCMP(ptr, "if", 2) == 0 || STRNCMP(ptr, "el", 2) == 0)
  1570.             hash_dir = 1;
  1571.         else if (STRNCMP(ptr, "endif", 5) == 0)
  1572.             hash_dir = -1;
  1573.         else
  1574.             return NULL;
  1575.         }
  1576.         pos.col = 0;
  1577.         while (!got_int)
  1578.         {
  1579.         if (hash_dir > 0)
  1580.         {
  1581.             if (pos.lnum == curbuf->b_ml.ml_line_count)
  1582.             break;
  1583.         }
  1584.         else if (pos.lnum == 1)
  1585.             break;
  1586.         pos.lnum += hash_dir;
  1587.         linep = ml_get(pos.lnum);
  1588.         line_breakcheck();    /* check for CTRL-C typed */
  1589.         ptr = skipwhite(linep);
  1590.         if (*ptr != '#')
  1591.             continue;
  1592.         pos.col = (colnr_T) (ptr - linep);
  1593.         ptr = skipwhite(ptr + 1);
  1594.         if (hash_dir > 0)
  1595.         {
  1596.             if (STRNCMP(ptr, "if", 2) == 0)
  1597.             count++;
  1598.             else if (STRNCMP(ptr, "el", 2) == 0)
  1599.             {
  1600.             if (count == 0)
  1601.                 return &pos;
  1602.             }
  1603.             else if (STRNCMP(ptr, "endif", 5) == 0)
  1604.             {
  1605.             if (count == 0)
  1606.                 return &pos;
  1607.             count--;
  1608.             }
  1609.         }
  1610.         else
  1611.         {
  1612.             if (STRNCMP(ptr, "if", 2) == 0)
  1613.             {
  1614.             if (count == 0)
  1615.                 return &pos;
  1616.             count--;
  1617.             }
  1618.             else if (initc == '#' && STRNCMP(ptr, "el", 2) == 0)
  1619.             {
  1620.             if (count == 0)
  1621.                 return &pos;
  1622.             }
  1623.             else if (STRNCMP(ptr, "endif", 5) == 0)
  1624.             count++;
  1625.         }
  1626.         }
  1627.         return NULL;
  1628.     }
  1629.     }
  1630.  
  1631. #ifdef FEAT_RIGHTLEFT
  1632.     /* This is just guessing: when 'rightleft' is set, search for a maching
  1633.      * paren/brace in the other direction. */
  1634.     if (curwin->w_p_rl && vim_strchr((char_u *)"()[]{}<>", initc) != NULL)
  1635.     backwards = !backwards;
  1636. #endif
  1637.  
  1638.     do_quotes = -1;
  1639.     start_in_quotes = MAYBE;
  1640.     /* backward search: Check if this line contains a single-line comment */
  1641.     if (backwards && comment_dir)
  1642.     comment_col = check_linecomment(linep);
  1643.     while (!got_int)
  1644.     {
  1645.     /*
  1646.      * Go to the next position, forward or backward. We could use
  1647.      * inc() and dec() here, but that is much slower
  1648.      */
  1649.     if (backwards)
  1650.     {
  1651.         if (pos.col == 0)        /* at start of line, go to prev. one */
  1652.         {
  1653.         if (pos.lnum == 1)    /* start of file */
  1654.             break;
  1655.         --pos.lnum;
  1656.  
  1657.         if (maxtravel && traveled++ > maxtravel)
  1658.             break;
  1659.  
  1660.         linep = ml_get(pos.lnum);
  1661.         pos.col = (colnr_T)STRLEN(linep); /* pos.col on trailing NUL */
  1662.         do_quotes = -1;
  1663.         line_breakcheck();
  1664.  
  1665.         /* Check if this line contains a single-line comment */
  1666.         if (comment_dir)
  1667.             comment_col = check_linecomment(linep);
  1668.         }
  1669.         else
  1670.         {
  1671.         --pos.col;
  1672. #ifdef FEAT_MBYTE
  1673.         if (has_mbyte)
  1674.             pos.col -= (*mb_head_off)(linep, linep + pos.col);
  1675. #endif
  1676.         }
  1677.     }
  1678.     else                /* forward search */
  1679.     {
  1680.         if (linep[pos.col] == NUL)    /* at end of line, go to next one */
  1681.         {
  1682.         if (pos.lnum == curbuf->b_ml.ml_line_count) /* end of file */
  1683.             break;
  1684.         ++pos.lnum;
  1685.  
  1686.         if (maxtravel && traveled++ > maxtravel)
  1687.             break;
  1688.  
  1689.         linep = ml_get(pos.lnum);
  1690.         pos.col = 0;
  1691.         do_quotes = -1;
  1692.         line_breakcheck();
  1693.         }
  1694.         else
  1695.         {
  1696. #ifdef FEAT_MBYTE
  1697.         if (has_mbyte)
  1698.             pos.col += (*mb_ptr2len_check)(linep + pos.col);
  1699.         else
  1700. #endif
  1701.             ++pos.col;
  1702.         }
  1703.     }
  1704.  
  1705.     /*
  1706.      * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0.
  1707.      */
  1708.     if (pos.col == 0 && (flags & FM_BLOCKSTOP) &&
  1709.                      (linep[0] == '{' || linep[0] == '}'))
  1710.     {
  1711.         if (linep[0] == findc && count == 0)    /* match! */
  1712.         return &pos;
  1713.         break;                    /* out of scope */
  1714.     }
  1715.  
  1716.     if (comment_dir)
  1717.     {
  1718.         /* Note: comments do not nest, and we ignore quotes in them */
  1719.         /* TODO: ignore comment brackets inside strings */
  1720.         if (comment_dir == FORWARD)
  1721.         {
  1722.         if (linep[pos.col] == '*' && linep[pos.col + 1] == '/')
  1723.         {
  1724.             pos.col++;
  1725.             return &pos;
  1726.         }
  1727.         }
  1728.         else    /* Searching backwards */
  1729.         {
  1730.         /*
  1731.          * A comment may contain / * or / /, it may also start or end
  1732.          * with / * /.    Ignore a / * after / /.
  1733.          */
  1734.         if (pos.col == 0)
  1735.             continue;
  1736.         else if (  linep[pos.col - 1] == '/'
  1737.             && linep[pos.col] == '*'
  1738.             && (int)pos.col < comment_col)
  1739.         {
  1740.             count++;
  1741.             match_pos = pos;
  1742.             match_pos.col--;
  1743.         }
  1744.         else if (linep[pos.col - 1] == '*' && linep[pos.col] == '/')
  1745.         {
  1746.             if (count > 0)
  1747.             pos = match_pos;
  1748.             else if (pos.col > 1 && linep[pos.col - 2] == '/'
  1749.                            && (int)pos.col <= comment_col)
  1750.             pos.col -= 2;
  1751.             else if (ignore_cend)
  1752.             continue;
  1753.             else
  1754.             return NULL;
  1755.             return &pos;
  1756.         }
  1757.         }
  1758.         continue;
  1759.     }
  1760.  
  1761.     /*
  1762.      * If smart matching ('cpoptions' does not contain '%'), braces inside
  1763.      * of quotes are ignored, but only if there is an even number of
  1764.      * quotes in the line.
  1765.      */
  1766.     if (cpo_match)
  1767.         do_quotes = 0;
  1768.     else if (do_quotes == -1)
  1769.     {
  1770.         /*
  1771.          * Count the number of quotes in the line, skipping \" and '"'.
  1772.          * Watch out for "\\".
  1773.          */
  1774.         at_start = do_quotes;
  1775.         for (ptr = linep; *ptr; ++ptr)
  1776.         {
  1777.         if (ptr == linep + pos.col + backwards)
  1778.             at_start = (do_quotes & 1);
  1779.         if (*ptr == '"'
  1780.             && (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\''))
  1781.             ++do_quotes;
  1782.         if (*ptr == '\\' && ptr[1] != NUL)
  1783.             ++ptr;
  1784.         }
  1785.         do_quotes &= 1;        /* result is 1 with even number of quotes */
  1786.  
  1787.         /*
  1788.          * If we find an uneven count, check current line and previous
  1789.          * one for a '\' at the end.
  1790.          */
  1791.         if (!do_quotes)
  1792.         {
  1793.         inquote = FALSE;
  1794.         if (ptr[-1] == '\\')
  1795.         {
  1796.             do_quotes = 1;
  1797.             if (start_in_quotes == MAYBE)
  1798.             {
  1799.             /* Do we need to use at_start here? */
  1800.             inquote = TRUE;
  1801.             start_in_quotes = TRUE;
  1802.             }
  1803.             else if (backwards)
  1804.             inquote = TRUE;
  1805.         }
  1806.         if (pos.lnum > 1)
  1807.         {
  1808.             ptr = ml_get(pos.lnum - 1);
  1809.             if (*ptr && *(ptr + STRLEN(ptr) - 1) == '\\')
  1810.             {
  1811.             do_quotes = 1;
  1812.             if (start_in_quotes == MAYBE)
  1813.             {
  1814.                 inquote = at_start;
  1815.                 if (inquote)
  1816.                 start_in_quotes = TRUE;
  1817.             }
  1818.             else if (!backwards)
  1819.                 inquote = TRUE;
  1820.             }
  1821.         }
  1822.         }
  1823.     }
  1824.     if (start_in_quotes == MAYBE)
  1825.         start_in_quotes = FALSE;
  1826.  
  1827.     /*
  1828.      * If 'smartmatch' is set:
  1829.      *   Things inside quotes are ignored by setting 'inquote'.  If we
  1830.      *   find a quote without a preceding '\' invert 'inquote'.  At the
  1831.      *   end of a line not ending in '\' we reset 'inquote'.
  1832.      *
  1833.      *   In lines with an uneven number of quotes (without preceding '\')
  1834.      *   we do not know which part to ignore. Therefore we only set
  1835.      *   inquote if the number of quotes in a line is even, unless this
  1836.      *   line or the previous one ends in a '\'.  Complicated, isn't it?
  1837.      */
  1838.     switch (c = linep[pos.col])
  1839.     {
  1840.     case NUL:
  1841.         /* at end of line without trailing backslash, reset inquote */
  1842.         if (pos.col == 0 || linep[pos.col - 1] != '\\')
  1843.         {
  1844.         inquote = FALSE;
  1845.         start_in_quotes = FALSE;
  1846.         }
  1847.         break;
  1848.  
  1849.     case '"':
  1850.         /* a quote that is preceded with an odd number of backslashes is
  1851.          * ignored */
  1852.         if (do_quotes)
  1853.         {
  1854.         int col;
  1855.  
  1856.         c = 0;
  1857.         for (col = pos.col - 1; col >= 0; --col)
  1858.             if (linep[col] != '\\')
  1859.             break;
  1860.         if ((c & 1) == 0)
  1861.         {
  1862.             inquote = !inquote;
  1863.             start_in_quotes = FALSE;
  1864.         }
  1865.         }
  1866.         break;
  1867.  
  1868.     /*
  1869.      * If smart matching ('cpoptions' does not contain '%'):
  1870.      *   Skip things in single quotes: 'x' or '\x'.  Be careful for single
  1871.      *   single quotes, eg jon's.  Things like '\233' or '\x3f' are not
  1872.      *   skipped, there is never a brace in them.
  1873.      *   Ignore this when finding matches for `'.
  1874.      */
  1875.     case '\'':
  1876.         if (!cpo_match && initc != '\'' && findc != '\'')
  1877.         {
  1878.         if (backwards)
  1879.         {
  1880.             if (pos.col > 1)
  1881.             {
  1882.             if (linep[pos.col - 2] == '\'')
  1883.             {
  1884.                 pos.col -= 2;
  1885.                 break;
  1886.             }
  1887.             else if (linep[pos.col - 2] == '\\' &&
  1888.                     pos.col > 2 && linep[pos.col - 3] == '\'')
  1889.             {
  1890.                 pos.col -= 3;
  1891.                 break;
  1892.             }
  1893.             }
  1894.         }
  1895.         else if (linep[pos.col + 1])    /* forward search */
  1896.         {
  1897.             if (linep[pos.col + 1] == '\\' &&
  1898.                 linep[pos.col + 2] && linep[pos.col + 3] == '\'')
  1899.             {
  1900.             pos.col += 3;
  1901.             break;
  1902.             }
  1903.             else if (linep[pos.col + 2] == '\'')
  1904.             {
  1905.             pos.col += 2;
  1906.             break;
  1907.             }
  1908.         }
  1909.         }
  1910.         /* FALLTHROUGH */
  1911.  
  1912.     default:
  1913.         /* Check for match outside of quotes, and inside of
  1914.          * quotes when the start is also inside of quotes */
  1915.         if (!inquote || start_in_quotes == TRUE)
  1916.         {
  1917.         if (c == initc)
  1918.             count++;
  1919.         else if (c == findc)
  1920.         {
  1921.             if (count == 0)
  1922.             return &pos;
  1923.             count--;
  1924.         }
  1925.         }
  1926.     }
  1927.     }
  1928.  
  1929.     if (comment_dir == BACKWARD && count > 0)
  1930.     {
  1931.     pos = match_pos;
  1932.     return &pos;
  1933.     }
  1934.     return (pos_T *)NULL;    /* never found it */
  1935. }
  1936.  
  1937. /*
  1938.  * Check if line[] contains a / / comment.
  1939.  * Return MAXCOL if not, otherwise return the column.
  1940.  * TODO: skip strings.
  1941.  */
  1942.     static int
  1943. check_linecomment(line)
  1944.     char_u    *line;
  1945. {
  1946.     char_u  *p;
  1947.  
  1948.     p = line;
  1949.     while ((p = vim_strchr(p, '/')) != NULL)
  1950.     {
  1951.     if (p[1] == '/')
  1952.         break;
  1953.     ++p;
  1954.     }
  1955.  
  1956.     if (p == NULL)
  1957.     return MAXCOL;
  1958.     return (int)(p - line);
  1959. }
  1960.  
  1961. /*
  1962.  * Move cursor briefly to character matching the one under the cursor.
  1963.  * Used for Insert mode and "r" command.
  1964.  * Show the match only if it is visible on the screen.
  1965.  * If there isn't a match, then beep.
  1966.  */
  1967.     void
  1968. showmatch()
  1969. {
  1970.     pos_T       *lpos, save_cursor;
  1971.     pos_T        mpos;
  1972.     colnr_T        vcol;
  1973.     long        save_so;
  1974. #ifdef CURSOR_SHAPE
  1975.     int            save_state;
  1976. #endif
  1977.  
  1978.     if ((lpos = findmatch(NULL, NUL)) == NULL)        /* no match, so beep */
  1979.     vim_beep();
  1980.     else if (lpos->lnum >= curwin->w_topline)
  1981.     {
  1982.     if (!curwin->w_p_wrap)
  1983.         getvcol(curwin, lpos, NULL, &vcol, NULL);
  1984.     if (curwin->w_p_wrap || (vcol >= curwin->w_leftcol
  1985.                    && vcol < curwin->w_leftcol + W_WIDTH(curwin)))
  1986.     {
  1987.         mpos = *lpos;    /* save the pos, update_screen() may change it */
  1988.         save_cursor = curwin->w_cursor;
  1989.         save_so = p_so;
  1990.         ++curwin->w_virtcol;    /* for when 'cpo' contains '$': do
  1991.                        redraw the ')' */
  1992.         update_screen(VALID);    /* show the new char first */
  1993.  
  1994. #ifdef CURSOR_SHAPE
  1995.         save_state = State;
  1996.         State = SHOWMATCH;
  1997.         ui_cursor_shape();        /* may show different cursor shape */
  1998. #endif
  1999.         curwin->w_cursor = mpos;    /* move to matching char */
  2000.         p_so = 0;            /* don't use 'scrolloff' here */
  2001.         showruler(FALSE);
  2002.         setcursor();
  2003.         cursor_on();        /* make sure that the cursor is shown */
  2004.         out_flush();
  2005. #ifdef FEAT_GUI
  2006.         if (gui.in_use)
  2007.         {
  2008.         gui_update_cursor(TRUE, FALSE);
  2009.         gui_mch_flush();
  2010.         }
  2011. #endif
  2012.  
  2013.         /*
  2014.          * brief pause, unless 'm' is present in 'cpo' and a character is
  2015.          * available.
  2016.          */
  2017.         if (vim_strchr(p_cpo, CPO_SHOWMATCH) != NULL)
  2018.         ui_delay(p_mat * 100L, TRUE);
  2019.         else if (!char_avail())
  2020.         ui_delay(p_mat * 100L, FALSE);
  2021.         curwin->w_cursor = save_cursor;    /* restore cursor position */
  2022.         p_so = save_so;
  2023. #ifdef CURSOR_SHAPE
  2024.         State = save_state;
  2025.         ui_cursor_shape();        /* may show different cursor shape */
  2026. #endif
  2027.     }
  2028.     }
  2029. }
  2030.  
  2031. /*
  2032.  * findsent(dir, count) - Find the start of the next sentence in direction
  2033.  * 'dir' Sentences are supposed to end in ".", "!" or "?" followed by white
  2034.  * space or a line break. Also stop at an empty line.
  2035.  * Return OK if the next sentence was found.
  2036.  */
  2037.     int
  2038. findsent(dir, count)
  2039.     int        dir;
  2040.     long    count;
  2041. {
  2042.     pos_T    pos, tpos;
  2043.     int        c;
  2044.     int        (*func) __ARGS((pos_T *));
  2045.     int        startlnum;
  2046.     int        noskip = FALSE;        /* do not skip blanks */
  2047.     int        cpo_J;
  2048.  
  2049.     pos = curwin->w_cursor;
  2050.     if (dir == FORWARD)
  2051.     func = incl;
  2052.     else
  2053.     func = decl;
  2054.  
  2055.     while (count--)
  2056.     {
  2057.     /*
  2058.      * if on an empty line, skip upto a non-empty line
  2059.      */
  2060.     if (gchar_pos(&pos) == NUL)
  2061.     {
  2062.         do
  2063.         if ((*func)(&pos) == -1)
  2064.             break;
  2065.         while (gchar_pos(&pos) == NUL);
  2066.         if (dir == FORWARD)
  2067.         goto found;
  2068.     }
  2069.     /*
  2070.      * if on the start of a paragraph or a section and searching forward,
  2071.      * go to the next line
  2072.      */
  2073.     else if (dir == FORWARD && pos.col == 0 &&
  2074.                         startPS(pos.lnum, NUL, FALSE))
  2075.     {
  2076.         if (pos.lnum == curbuf->b_ml.ml_line_count)
  2077.         return FAIL;
  2078.         ++pos.lnum;
  2079.         goto found;
  2080.     }
  2081.     else if (dir == BACKWARD)
  2082.         decl(&pos);
  2083.  
  2084.     /* go back to the previous non-blank char */
  2085.     while ((c = gchar_pos(&pos)) == ' ' || c == '\t' ||
  2086.          (dir == BACKWARD && vim_strchr((char_u *)".!?)]\"'", c) != NULL))
  2087.     {
  2088.         if (decl(&pos) == -1)
  2089.         break;
  2090.         /* when going forward: Stop in front of empty line */
  2091.         if (lineempty(pos.lnum) && dir == FORWARD)
  2092.         {
  2093.         incl(&pos);
  2094.         goto found;
  2095.         }
  2096.     }
  2097.  
  2098.     /* remember the line where the search started */
  2099.     startlnum = pos.lnum;
  2100.     cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL;
  2101.  
  2102.     for (;;)        /* find end of sentence */
  2103.     {
  2104.         c = gchar_pos(&pos);
  2105.         if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE)))
  2106.         {
  2107.         if (dir == BACKWARD && pos.lnum != startlnum)
  2108.             ++pos.lnum;
  2109.         break;
  2110.         }
  2111.         if (c == '.' || c == '!' || c == '?')
  2112.         {
  2113.         tpos = pos;
  2114.         do
  2115.             if ((c = inc(&tpos)) == -1)
  2116.             break;
  2117.         while (vim_strchr((char_u *)")]\"'", c = gchar_pos(&tpos))
  2118.             != NULL);
  2119.         if (c == -1  || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL
  2120.             || (cpo_J && (c == ' ' && inc(&tpos) >= 0
  2121.                   && gchar_pos(&tpos) == ' ')))
  2122.         {
  2123.             pos = tpos;
  2124.             if (gchar_pos(&pos) == NUL) /* skip NUL at EOL */
  2125.             inc(&pos);
  2126.             break;
  2127.         }
  2128.         }
  2129.         if ((*func)(&pos) == -1)
  2130.         {
  2131.         if (count)
  2132.             return FAIL;
  2133.         noskip = TRUE;
  2134.         break;
  2135.         }
  2136.     }
  2137. found:
  2138.         /* skip white space */
  2139.     while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t'))
  2140.         if (incl(&pos) == -1)
  2141.         break;
  2142.     }
  2143.  
  2144.     setpcmark();
  2145.     curwin->w_cursor = pos;
  2146.     return OK;
  2147. }
  2148.  
  2149. /*
  2150.  * findpar(dir, count, what) - Find the next paragraph in direction 'dir'
  2151.  * Paragraphs are currently supposed to be separated by empty lines.
  2152.  * Return TRUE if the next paragraph was found.
  2153.  * If 'what' is '{' or '}' we go to the next section.
  2154.  * If 'both' is TRUE also stop at '}'.
  2155.  */
  2156.     int
  2157. findpar(oap, dir, count, what, both)
  2158.     oparg_T        *oap;
  2159.     int            dir;
  2160.     long        count;
  2161.     int            what;
  2162.     int            both;
  2163. {
  2164.     linenr_T    curr;
  2165.     int        did_skip;   /* TRUE after separating lines have been skipped */
  2166.     int        first;        /* TRUE on first line */
  2167.  
  2168.     curr = curwin->w_cursor.lnum;
  2169.  
  2170.     while (count--)
  2171.     {
  2172.     did_skip = FALSE;
  2173.     for (first = TRUE; ; first = FALSE)
  2174.     {
  2175.         if (*ml_get(curr) != NUL)
  2176.         did_skip = TRUE;
  2177.  
  2178.         if (!first && did_skip && startPS(curr, what, both))
  2179.         break;
  2180.  
  2181.         if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count)
  2182.         {
  2183.         if (count)
  2184.             return FALSE;
  2185.         curr -= dir;
  2186.         break;
  2187.         }
  2188.     }
  2189.     }
  2190.     setpcmark();
  2191.     if (both && *ml_get(curr) == '}')    /* include line with '}' */
  2192.     ++curr;
  2193.     curwin->w_cursor.lnum = curr;
  2194.     if (curr == curbuf->b_ml.ml_line_count && what != '}')
  2195.     {
  2196.     if ((curwin->w_cursor.col = (colnr_T)STRLEN(ml_get(curr))) != 0)
  2197.     {
  2198.         --curwin->w_cursor.col;
  2199.         oap->inclusive = TRUE;
  2200.     }
  2201.     }
  2202.     else
  2203.     curwin->w_cursor.col = 0;
  2204.     return TRUE;
  2205. }
  2206.  
  2207. /*
  2208.  * check if the string 's' is a nroff macro that is in option 'opt'
  2209.  */
  2210.     static int
  2211. inmacro(opt, s)
  2212.     char_u    *opt;
  2213.     char_u    *s;
  2214. {
  2215.     char_u    *macro;
  2216.  
  2217.     for (macro = opt; macro[0]; ++macro)
  2218.     {
  2219.     if (macro[0] == s[0] && (((s[1] == NUL || s[1] == ' ') &&
  2220.            (macro[1] == NUL || macro[1] == ' ')) || macro[1] == s[1]))
  2221.         break;
  2222.     ++macro;
  2223.     if (macro[0] == NUL)
  2224.         break;
  2225.     }
  2226.     return (macro[0] != NUL);
  2227. }
  2228.  
  2229. /*
  2230.  * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
  2231.  * If 'para' is '{' or '}' only check for sections.
  2232.  * If 'both' is TRUE also stop at '}'
  2233.  */
  2234.     int
  2235. startPS(lnum, para, both)
  2236.     linenr_T    lnum;
  2237.     int        para;
  2238.     int        both;
  2239. {
  2240.     char_u    *s;
  2241.  
  2242.     s = ml_get(lnum);
  2243.     if (*s == para || *s == '\f' || (both && *s == '}'))
  2244.     return TRUE;
  2245.     if (*s == '.' && (inmacro(p_sections, s + 1) ||
  2246.                        (!para && inmacro(p_para, s + 1))))
  2247.     return TRUE;
  2248.     return FALSE;
  2249. }
  2250.  
  2251. /*
  2252.  * The following routines do the word searches performed by the 'w', 'W',
  2253.  * 'b', 'B', 'e', and 'E' commands.
  2254.  */
  2255.  
  2256. /*
  2257.  * To perform these searches, characters are placed into one of three
  2258.  * classes, and transitions between classes determine word boundaries.
  2259.  *
  2260.  * The classes are:
  2261.  *
  2262.  * 0 - white space
  2263.  * 1 - punctuation
  2264.  * 2 or higher - keyword characters (letters, digits and underscore)
  2265.  */
  2266.  
  2267. static int    cls_bigword;    /* TRUE for "W", "B" or "E" */
  2268.  
  2269. /*
  2270.  * cls() - returns the class of character at curwin->w_cursor
  2271.  *
  2272.  * If a 'W', 'B', or 'E' motion is being done (cls_bigword == TRUE), chars
  2273.  * from class 2 and higher are reported as class 1 since only white space
  2274.  * boundaries are of interest.
  2275.  */
  2276.     static int
  2277. cls()
  2278. {
  2279.     int        c;
  2280.  
  2281.     c = gchar_cursor();
  2282. #ifdef FEAT_FKMAP    /* when 'akm' (Farsi mode), take care of Farsi blank */
  2283.     if (p_altkeymap && c == F_BLANK)
  2284.     return 0;
  2285. #endif
  2286.     if (c == ' ' || c == '\t' || c == NUL)
  2287.     return 0;
  2288. #ifdef FEAT_MBYTE
  2289.     if (enc_dbcs != 0 && c > 0xFF)
  2290.     {
  2291.     /* If cls_bigword, report multi-byte chars as class 1. */
  2292.     if (enc_dbcs == DBCS_KOR && cls_bigword)
  2293.         return 1;
  2294.  
  2295.     /* process code leading/trailing bytes */
  2296.     return dbcs_class(((unsigned)c >> 8), (c & 0xFF));
  2297.     }
  2298.     if (enc_utf8)
  2299.     {
  2300.     c = utf_class(c);
  2301.     if (c != 0 && cls_bigword)
  2302.         return 1;
  2303.     return c;
  2304.     }
  2305. #endif
  2306.  
  2307.     /* If cls_bigword is TRUE, report all non-blanks as class 1. */
  2308.     if (cls_bigword)
  2309.     return 1;
  2310.  
  2311.     if (vim_iswordc(c))
  2312.     return 2;
  2313.     return 1;
  2314. }
  2315.  
  2316.  
  2317. /*
  2318.  * fwd_word(count, type, eol) - move forward one word
  2319.  *
  2320.  * Returns FAIL if the cursor was already at the end of the file.
  2321.  * If eol is TRUE, last word stops at end of line (for operators).
  2322.  */
  2323.     int
  2324. fwd_word(count, bigword, eol)
  2325.     long    count;
  2326.     int        bigword;    /* "W", "E" or "B" */
  2327.     int        eol;
  2328. {
  2329.     int        sclass;        /* starting class */
  2330.     int        i;
  2331.     int        last_line;
  2332.  
  2333. #ifdef FEAT_VIRTUALEDIT
  2334.     curwin->w_cursor.coladd = 0;
  2335. #endif
  2336.     cls_bigword = bigword;
  2337.     while (--count >= 0)
  2338.     {
  2339. #ifdef FEAT_FOLDING
  2340.     /* When inside a range of folded lines, move to the last char of the
  2341.      * last line. */
  2342.     if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
  2343.         coladvance((colnr_T)MAXCOL);
  2344. #endif
  2345.     sclass = cls();
  2346.  
  2347.     /*
  2348.      * We always move at least one character, unless on the last
  2349.      * character in the buffer.
  2350.      */
  2351.     last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
  2352.     i = inc_cursor();
  2353.     if (i == -1 || (i >= 1 && last_line)) /* started at last char in file */
  2354.         return FAIL;
  2355.     if (i == 1 && eol && count == 0)      /* started at last char in line */
  2356.         return OK;
  2357.  
  2358.     /*
  2359.      * Go one char past end of current word (if any)
  2360.      */
  2361.     if (sclass != 0)
  2362.         while (cls() == sclass)
  2363.         {
  2364.         i = inc_cursor();
  2365.         if (i == -1 || (i >= 1 && eol && count == 0))
  2366.             return OK;
  2367.         }
  2368.  
  2369.     /*
  2370.      * go to next non-white
  2371.      */
  2372.     while (cls() == 0)
  2373.     {
  2374.         /*
  2375.          * We'll stop if we land on a blank line
  2376.          */
  2377.         if (curwin->w_cursor.col == 0 && *ml_get_curline() == NUL)
  2378.         break;
  2379.  
  2380.         i = inc_cursor();
  2381.         if (i == -1 || (i >= 1 && eol && count == 0))
  2382.         return OK;
  2383.     }
  2384.     }
  2385.     return OK;
  2386. }
  2387.  
  2388. /*
  2389.  * bck_word() - move backward 'count' words
  2390.  *
  2391.  * If stop is TRUE and we are already on the start of a word, move one less.
  2392.  *
  2393.  * Returns FAIL if top of the file was reached.
  2394.  */
  2395.     int
  2396. bck_word(count, bigword, stop)
  2397.     long    count;
  2398.     int        bigword;
  2399.     int        stop;
  2400. {
  2401.     int        sclass;        /* starting class */
  2402.  
  2403. #ifdef FEAT_VIRTUALEDIT
  2404.     curwin->w_cursor.coladd = 0;
  2405. #endif
  2406.     cls_bigword = bigword;
  2407.     while (--count >= 0)
  2408.     {
  2409. #ifdef FEAT_FOLDING
  2410.     /* When inside a range of folded lines, move to the first char of the
  2411.      * first line. */
  2412.     if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL))
  2413.         curwin->w_cursor.col = 0;
  2414. #endif
  2415.     sclass = cls();
  2416.     if (dec_cursor() == -1)        /* started at start of file */
  2417.         return FAIL;
  2418.  
  2419.     if (!stop || sclass == cls() || sclass == 0)
  2420.     {
  2421.         /*
  2422.          * Skip white space before the word.
  2423.          * Stop on an empty line.
  2424.          */
  2425.         while (cls() == 0)
  2426.         {
  2427.         if (curwin->w_cursor.col == 0
  2428.                       && lineempty(curwin->w_cursor.lnum))
  2429.             goto finished;
  2430.         if (dec_cursor() == -1) /* hit start of file, stop here */
  2431.             return OK;
  2432.         }
  2433.  
  2434.         /*
  2435.          * Move backward to start of this word.
  2436.          */
  2437.         if (skip_chars(cls(), BACKWARD))
  2438.         return OK;
  2439.     }
  2440.  
  2441.     inc_cursor();            /* overshot - forward one */
  2442. finished:
  2443.     stop = FALSE;
  2444.     }
  2445.     return OK;
  2446. }
  2447.  
  2448. /*
  2449.  * end_word() - move to the end of the word
  2450.  *
  2451.  * There is an apparent bug in the 'e' motion of the real vi. At least on the
  2452.  * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
  2453.  * motion crosses blank lines. When the real vi crosses a blank line in an
  2454.  * 'e' motion, the cursor is placed on the FIRST character of the next
  2455.  * non-blank line. The 'E' command, however, works correctly. Since this
  2456.  * appears to be a bug, I have not duplicated it here.
  2457.  *
  2458.  * Returns FAIL if end of the file was reached.
  2459.  *
  2460.  * If stop is TRUE and we are already on the end of a word, move one less.
  2461.  * If empty is TRUE stop on an empty line.
  2462.  */
  2463.     int
  2464. end_word(count, bigword, stop, empty)
  2465.     long    count;
  2466.     int        bigword;
  2467.     int        stop;
  2468.     int        empty;
  2469. {
  2470.     int        sclass;        /* starting class */
  2471.  
  2472. #ifdef FEAT_VIRTUALEDIT
  2473.     curwin->w_cursor.coladd = 0;
  2474. #endif
  2475.     cls_bigword = bigword;
  2476.     while (--count >= 0)
  2477.     {
  2478. #ifdef FEAT_FOLDING
  2479.     /* When inside a range of folded lines, move to the last char of the
  2480.      * last line. */
  2481.     if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum))
  2482.         coladvance((colnr_T)MAXCOL);
  2483. #endif
  2484.     sclass = cls();
  2485.     if (inc_cursor() == -1)
  2486.         return FAIL;
  2487.  
  2488.     /*
  2489.      * If we're in the middle of a word, we just have to move to the end
  2490.      * of it.
  2491.      */
  2492.     if (cls() == sclass && sclass != 0)
  2493.     {
  2494.         /*
  2495.          * Move forward to end of the current word
  2496.          */
  2497.         if (skip_chars(sclass, FORWARD))
  2498.         return FAIL;
  2499.     }
  2500.     else if (!stop || sclass == 0)
  2501.     {
  2502.         /*
  2503.          * We were at the end of a word. Go to the end of the next word.
  2504.          * First skip white space, if 'empty' is TRUE, stop at empty line.
  2505.          */
  2506.         while (cls() == 0)
  2507.         {
  2508.         if (empty && curwin->w_cursor.col == 0
  2509.                       && lineempty(curwin->w_cursor.lnum))
  2510.             goto finished;
  2511.         if (inc_cursor() == -1)        /* hit end of file, stop here */
  2512.             return FAIL;
  2513.         }
  2514.  
  2515.         /*
  2516.          * Move forward to the end of this word.
  2517.          */
  2518.         if (skip_chars(cls(), FORWARD))
  2519.         return FAIL;
  2520.     }
  2521.     dec_cursor();            /* overshot - one char backward */
  2522. finished:
  2523.     stop = FALSE;            /* we move only one word less */
  2524.     }
  2525.     return OK;
  2526. }
  2527.  
  2528. /*
  2529.  * Move back to the end of the word.
  2530.  *
  2531.  * Returns FAIL if start of the file was reached.
  2532.  */
  2533.     int
  2534. bckend_word(count, bigword, eol)
  2535.     long    count;
  2536.     int        bigword;    /* TRUE for "B" */
  2537.     int        eol;        /* TRUE: stop at end of line. */
  2538. {
  2539.     int        sclass;        /* starting class */
  2540.     int        i;
  2541.  
  2542. #ifdef FEAT_VIRTUALEDIT
  2543.     curwin->w_cursor.coladd = 0;
  2544. #endif
  2545.     cls_bigword = bigword;
  2546.     while (--count >= 0)
  2547.     {
  2548.     sclass = cls();
  2549.     if ((i = dec_cursor()) == -1)
  2550.         return FAIL;
  2551.     if (eol && i == 1)
  2552.         return OK;
  2553.  
  2554.     /*
  2555.      * Move backward to before the start of this word.
  2556.      */
  2557.     if (sclass != 0)
  2558.     {
  2559.         while (cls() == sclass)
  2560.         if ((i = dec_cursor()) == -1 || (eol && i == 1))
  2561.             return OK;
  2562.     }
  2563.  
  2564.     /*
  2565.      * Move backward to end of the previous word
  2566.      */
  2567.     while (cls() == 0)
  2568.     {
  2569.         if (curwin->w_cursor.col == 0 && lineempty(curwin->w_cursor.lnum))
  2570.         break;
  2571.         if ((i = dec_cursor()) == -1 || (eol && i == 1))
  2572.         return OK;
  2573.     }
  2574.     }
  2575.     return OK;
  2576. }
  2577.  
  2578. /*
  2579.  * Skip a row of characters of the same class.
  2580.  * Return TRUE when end-of-file reached, FALSE otherwise.
  2581.  */
  2582.     static int
  2583. skip_chars(cclass, dir)
  2584.     int        cclass;
  2585.     int        dir;
  2586. {
  2587.     while (cls() == cclass)
  2588.     if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1)
  2589.         return TRUE;
  2590.     return FALSE;
  2591. }
  2592.  
  2593. #ifdef FEAT_TEXTOBJ
  2594. /*
  2595.  * Go back to the start of the word or the start of white space
  2596.  */
  2597.     static void
  2598. back_in_line()
  2599. {
  2600.     int        sclass;            /* starting class */
  2601.  
  2602.     sclass = cls();
  2603.     for (;;)
  2604.     {
  2605.     if (curwin->w_cursor.col == 0)        /* stop at start of line */
  2606.         break;
  2607.     --curwin->w_cursor.col;
  2608.     if (cls() != sclass)            /* stop at start of word */
  2609.     {
  2610.         ++curwin->w_cursor.col;
  2611.         break;
  2612.     }
  2613.     }
  2614. }
  2615.  
  2616.     static void
  2617. find_first_blank(posp)
  2618.     pos_T    *posp;
  2619. {
  2620.     int        c;
  2621.  
  2622.     while (decl(posp) != -1)
  2623.     {
  2624.     c = gchar_pos(posp);
  2625.     if (!vim_iswhite(c))
  2626.     {
  2627.         incl(posp);
  2628.         break;
  2629.     }
  2630.     }
  2631. }
  2632.  
  2633. /*
  2634.  * Skip count/2 sentences and count/2 separating white spaces.
  2635.  */
  2636.     static void
  2637. findsent_forward(count, at_start_sent)
  2638.     long    count;
  2639.     int        at_start_sent;    /* cursor is at start of sentence */
  2640. {
  2641.     while (count--)
  2642.     {
  2643.     findsent(FORWARD, 1L);
  2644.     if (at_start_sent)
  2645.         find_first_blank(&curwin->w_cursor);
  2646.     if (count == 0 || at_start_sent)
  2647.         decl(&curwin->w_cursor);
  2648.     at_start_sent = !at_start_sent;
  2649.     }
  2650. }
  2651.  
  2652. /*
  2653.  * Find word under cursor, cursor at end.
  2654.  * Used while an operator is pending, and in Visual mode.
  2655.  */
  2656.     int
  2657. current_word(oap, count, include, bigword)
  2658.     oparg_T    *oap;
  2659.     long    count;
  2660.     int        include;    /* TRUE: include word and white space */
  2661.     int        bigword;    /* FALSE == word, TRUE == WORD */
  2662. {
  2663.     pos_T    start_pos;
  2664.     pos_T    pos;
  2665.     int        inclusive = TRUE;
  2666.  
  2667.     cls_bigword = bigword;
  2668.  
  2669. #ifdef FEAT_VISUAL
  2670.     /* Correct cursor when 'selection' is exclusive */
  2671.     if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor))
  2672.     dec_cursor();
  2673.  
  2674.     /*
  2675.      * When Visual mode is not active, or when the VIsual area is only one
  2676.      * character, select the word and/or white space under the cursor.
  2677.      */
  2678.     if (!VIsual_active || equal(curwin->w_cursor, VIsual))
  2679. #endif
  2680.     {
  2681.     /*
  2682.      * Go to start of current word or white space.
  2683.      */
  2684.     back_in_line();
  2685.     start_pos = curwin->w_cursor;
  2686.  
  2687.     /*
  2688.      * If the start is on white space, and white space should be included
  2689.      * ("    word"), or start is not on white space, and white space should
  2690.      * not be included ("word"), find end of word.
  2691.      */
  2692.     if ((cls() == 0) == include)
  2693.     {
  2694.         if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
  2695.         return FAIL;
  2696.     }
  2697.     else
  2698.     {
  2699.         /*
  2700.          * If the start is not on white space, and white space should be
  2701.          * included ("word     "), or start is on white space and white
  2702.          * space should not be included ("     "), find start of word.
  2703.          */
  2704.         if (fwd_word(1L, bigword, TRUE) == FAIL)
  2705.         return FAIL;
  2706.         /*
  2707.          * If end is just past a new-line, we don't want to include the
  2708.          * first character on the line
  2709.          */
  2710.         if (oneleft() == FAIL)    /* put cursor on last char of area */
  2711.         inclusive = FALSE;
  2712.         else if (include)
  2713.         {
  2714.         /*
  2715.          * If we don't include white space at the end, move the start
  2716.          * to include some white space there. This makes "daw" work
  2717.          * better on the last word in a sentence. Don't delete white
  2718.          * space at start of line (indent).
  2719.          */
  2720.         if (cls() != 0)
  2721.         {
  2722.             pos = curwin->w_cursor;    /* save cursor position */
  2723.             curwin->w_cursor = start_pos;
  2724.             if (oneleft() == OK)
  2725.             {
  2726.             back_in_line();
  2727.             if (cls() == 0 && curwin->w_cursor.col > 0)
  2728.                 start_pos = curwin->w_cursor;
  2729.             }
  2730.             curwin->w_cursor = pos;    /* put cursor back at end */
  2731.         }
  2732.         }
  2733.     }
  2734.  
  2735. #ifdef FEAT_VISUAL
  2736.     if (VIsual_active)
  2737.     {
  2738.         /* should do something when inclusive == FALSE ! */
  2739.         VIsual = start_pos;
  2740.         redraw_curbuf_later(INVERTED);    /* update the inversion */
  2741.     }
  2742.     else
  2743. #endif
  2744.     {
  2745.         oap->start = start_pos;
  2746.         oap->motion_type = MCHAR;
  2747.     }
  2748.     --count;
  2749.     }
  2750.  
  2751.     /*
  2752.      * When count is still > 0, extend with more objects.
  2753.      */
  2754.     while (count > 0)
  2755.     {
  2756.     inclusive = TRUE;
  2757. #ifdef FEAT_VISUAL
  2758.     if (VIsual_active && lt(curwin->w_cursor, VIsual))
  2759.     {
  2760.         /*
  2761.          * In Visual mode, with cursor at start: move cursor back.
  2762.          */
  2763.         if (decl(&curwin->w_cursor) == -1)
  2764.         return FAIL;
  2765.         if (include != (cls() != 0))
  2766.         {
  2767.         if (bck_word(1L, bigword, TRUE) == FAIL)
  2768.             return FAIL;
  2769.         }
  2770.         else
  2771.         {
  2772.         if (bckend_word(1L, bigword, TRUE) == FAIL)
  2773.             return FAIL;
  2774.         (void)incl(&curwin->w_cursor);
  2775.         }
  2776.     }
  2777.     else
  2778. #endif
  2779.     {
  2780.         /*
  2781.          * Move cursor forward one word and/or white area.
  2782.          */
  2783.         if (incl(&curwin->w_cursor) == -1)
  2784.         return FAIL;
  2785.         if (include != (cls() == 0))
  2786.         {
  2787.         if (fwd_word(1L, bigword, TRUE) == FAIL)
  2788.             return FAIL;
  2789.         /*
  2790.          * If end is just past a new-line, we don't want to include
  2791.          * the first character on the line
  2792.          */
  2793.         if (oneleft() == FAIL)    /* put cursor on last char of white */
  2794.             inclusive = FALSE;
  2795.         }
  2796.         else
  2797.         {
  2798.         if (end_word(1L, bigword, TRUE, TRUE) == FAIL)
  2799.             return FAIL;
  2800.         }
  2801.     }
  2802.     --count;
  2803.     }
  2804. #ifdef FEAT_VISUAL
  2805.     if (VIsual_active)
  2806.     {
  2807.     if (*p_sel == 'e' && inclusive && ltoreq(VIsual, curwin->w_cursor))
  2808.         inc_cursor();
  2809.     if (VIsual_mode == 'V')
  2810.     {
  2811.         VIsual_mode = 'v';
  2812.         redraw_cmdline = TRUE;        /* show mode later */
  2813.     }
  2814.     }
  2815.     else
  2816. #endif
  2817.     oap->inclusive = inclusive;
  2818.  
  2819.     return OK;
  2820. }
  2821.  
  2822. /*
  2823.  * Find sentence(s) under the cursor, cursor at end.
  2824.  * When Visual active, extend it by one or more sentences.
  2825.  */
  2826.     int
  2827. current_sent(oap, count, include)
  2828.     oparg_T   *oap;
  2829.     long    count;
  2830.     int        include;
  2831. {
  2832.     pos_T    start_pos;
  2833.     pos_T    pos;
  2834.     int        start_blank;
  2835.     int        c;
  2836.     int        at_start_sent;
  2837.     long    ncount;
  2838.  
  2839.     start_pos = curwin->w_cursor;
  2840.     pos = start_pos;
  2841.     findsent(FORWARD, 1L);    /* Find start of next sentence. */
  2842.  
  2843. #ifdef FEAT_VISUAL
  2844.     /*
  2845.      * When visual area is bigger than one character: Extend it.
  2846.      */
  2847.     if (VIsual_active && !equal(start_pos, VIsual))
  2848.     {
  2849. extend:
  2850.     if (lt(start_pos, VIsual))
  2851.     {
  2852.         /*
  2853.          * Cursor at start of Visual area.
  2854.          * Find out where we are:
  2855.          * - in the white space before a sentence
  2856.          * - in a sentence or just after it
  2857.          * - at the start of a sentence
  2858.          */
  2859.         at_start_sent = TRUE;
  2860.         decl(&pos);
  2861.         while (lt(pos, curwin->w_cursor))
  2862.         {
  2863.         c = gchar_pos(&pos);
  2864.         if (!vim_iswhite(c))
  2865.         {
  2866.             at_start_sent = FALSE;
  2867.             break;
  2868.         }
  2869.         incl(&pos);
  2870.         }
  2871.         if (!at_start_sent)
  2872.         {
  2873.         findsent(BACKWARD, 1L);
  2874.         if (equal(curwin->w_cursor, start_pos))
  2875.             at_start_sent = TRUE;  /* exactly at start of sentence */
  2876.         else
  2877.             /* inside a sentence, go to its end (start of next) */
  2878.             findsent(FORWARD, 1L);
  2879.         }
  2880.         if (include)    /* "as" gets twice as much as "is" */
  2881.         count *= 2;
  2882.         while (count--)
  2883.         {
  2884.         if (at_start_sent)
  2885.             find_first_blank(&curwin->w_cursor);
  2886.         c = gchar_cursor();
  2887.         if (!at_start_sent || (!include && !vim_iswhite(c)))
  2888.             findsent(BACKWARD, 1L);
  2889.         at_start_sent = !at_start_sent;
  2890.         }
  2891.     }
  2892.     else
  2893.     {
  2894.         /*
  2895.          * Cursor at end of Visual area.
  2896.          * Find out where we are:
  2897.          * - just before a sentence
  2898.          * - just before or in the white space before a sentence
  2899.          * - in a sentence
  2900.          */
  2901.         incl(&pos);
  2902.         at_start_sent = TRUE;
  2903.         if (!equal(pos, curwin->w_cursor))    /* not just before a sentence */
  2904.         {
  2905.         at_start_sent = FALSE;
  2906.         while (lt(pos, curwin->w_cursor))
  2907.         {
  2908.             c = gchar_pos(&pos);
  2909.             if (!vim_iswhite(c))
  2910.             {
  2911.             at_start_sent = TRUE;
  2912.             break;
  2913.             }
  2914.             incl(&pos);
  2915.         }
  2916.         if (at_start_sent)    /* in the sentence */
  2917.             findsent(BACKWARD, 1L);
  2918.         else        /* in/before white before a sentence */
  2919.             curwin->w_cursor = start_pos;
  2920.         }
  2921.  
  2922.         if (include)    /* "as" gets twice as much as "is" */
  2923.         count *= 2;
  2924.         findsent_forward(count, at_start_sent);
  2925.         if (*p_sel == 'e')
  2926.         ++curwin->w_cursor.col;
  2927.     }
  2928.     return OK;
  2929.     }
  2930. #endif
  2931.  
  2932.     /*
  2933.      * If cursor started on blank, check if it is just before the start of the
  2934.      * next sentence.
  2935.      */
  2936.     while (c = gchar_pos(&pos), vim_iswhite(c))    /* vim_iswhite() is a macro */
  2937.     incl(&pos);
  2938.     if (equal(pos, curwin->w_cursor))
  2939.     {
  2940.     start_blank = TRUE;
  2941.     find_first_blank(&start_pos);    /* go back to first blank */
  2942.     }
  2943.     else
  2944.     {
  2945.     start_blank = FALSE;
  2946.     findsent(BACKWARD, 1L);
  2947.     start_pos = curwin->w_cursor;
  2948.     }
  2949.     if (include)
  2950.     ncount = count * 2;
  2951.     else
  2952.     ncount = count;
  2953.     if (!include && start_blank)
  2954.     --ncount;
  2955.     if (ncount)
  2956.     findsent_forward(ncount, TRUE);
  2957.  
  2958.     if (include)
  2959.     {
  2960.     /*
  2961.      * If the blank in front of the sentence is included, exclude the
  2962.      * blanks at the end of the sentence, go back to the first blank.
  2963.      * If there are no trailing blanks, try to include leading blanks.
  2964.      */
  2965.     if (start_blank)
  2966.         find_first_blank(&curwin->w_cursor);
  2967.     else if (c = gchar_cursor(), !vim_iswhite(c))
  2968.         find_first_blank(&start_pos);
  2969.     }
  2970.  
  2971. #ifdef FEAT_VISUAL
  2972.     if (VIsual_active)
  2973.     {
  2974.     /* avoid getting stuck with "is" on a single space before a sent. */
  2975.     if (equal(start_pos, curwin->w_cursor))
  2976.         goto extend;
  2977.     if (*p_sel == 'e')
  2978.         ++curwin->w_cursor.col;
  2979.     VIsual = start_pos;
  2980.     VIsual_mode = 'v';
  2981.     redraw_curbuf_later(INVERTED);    /* update the inversion */
  2982.     }
  2983.     else
  2984. #endif
  2985.     {
  2986.     /* include a newline after the sentence, if there is one */
  2987.     if (incl(&curwin->w_cursor) == -1)
  2988.         oap->inclusive = TRUE;
  2989.     else
  2990.         oap->inclusive = FALSE;
  2991.     oap->start = start_pos;
  2992.     oap->motion_type = MCHAR;
  2993.     }
  2994.     return OK;
  2995. }
  2996.  
  2997.     int
  2998. current_block(oap, count, include, what, other)
  2999.     oparg_T    *oap;
  3000.     long    count;
  3001.     int        include;    /* TRUE == include white space */
  3002.     int        what;        /* '(', '{', etc. */
  3003.     int        other;        /* ')', '}', etc. */
  3004. {
  3005.     pos_T    old_pos;
  3006.     pos_T    *pos = NULL;
  3007.     pos_T    start_pos;
  3008.     pos_T    *end_pos;
  3009.     pos_T    old_start, old_end;
  3010.     char_u    *save_cpo;
  3011.  
  3012.     old_pos = curwin->w_cursor;
  3013.     old_end = curwin->w_cursor;            /* remember where we started */
  3014.     old_start = old_end;
  3015.  
  3016.     /*
  3017.      * If we start on '(', '{', ')', '}', etc., use the whole block inclusive.
  3018.      */
  3019. #ifdef FEAT_VISUAL
  3020.     if (!VIsual_active || equal(VIsual, curwin->w_cursor))
  3021. #endif
  3022.     {
  3023.     setpcmark();
  3024.     if (what == '{')            /* ignore indent */
  3025.         while (inindent(1))
  3026.         if (inc_cursor() != 0)
  3027.             break;
  3028.     if (gchar_cursor() == what)        /* cursor on '(' or '{' */
  3029.         ++curwin->w_cursor.col;
  3030.     }
  3031. #ifdef FEAT_VISUAL
  3032.     else if (lt(VIsual, curwin->w_cursor))
  3033.     {
  3034.     old_start = VIsual;
  3035.     curwin->w_cursor = VIsual;        /* cursor at low end of Visual */
  3036.     }
  3037.     else
  3038.     old_end = VIsual;
  3039. #endif
  3040.  
  3041.     /*
  3042.      * Search backwards for unclosed '(', '{', etc..
  3043.      * Put this position in start_pos.
  3044.      * Ignory quotes here.
  3045.      */
  3046.     save_cpo = p_cpo;
  3047.     p_cpo = (char_u *)"%";
  3048.     while (count-- > 0)
  3049.     {
  3050.     if ((pos = findmatch(NULL, what)) == NULL)
  3051.         break;
  3052.     curwin->w_cursor = *pos;
  3053.     start_pos = *pos;   /* the findmatch for end_pos will overwrite *pos */
  3054.     }
  3055.     p_cpo = save_cpo;
  3056.  
  3057.     /*
  3058.      * Search for matching ')', '}', etc.
  3059.      * Put this position in curwin->w_cursor.
  3060.      */
  3061.     if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL)
  3062.     {
  3063.     curwin->w_cursor = old_pos;
  3064.     return FAIL;
  3065.     }
  3066.     curwin->w_cursor = *end_pos;
  3067.  
  3068.     /*
  3069.      * Try to exclude the '(', '{', ')', '}', etc. when "include" is FALSE.
  3070.      * If the ending '}' is only preceded by indent, skip that indent.
  3071.      * But only if the resulting area is not smaller than what we started with.
  3072.      */
  3073.     while (!include)
  3074.     {
  3075.     incl(&start_pos);
  3076.     decl(&curwin->w_cursor);
  3077.     if (what == '{')
  3078.         while (inindent(1))
  3079.         if (decl(&curwin->w_cursor) != 0)
  3080.             break;
  3081. #ifdef FEAT_VISUAL
  3082.     /*
  3083.      * In Visual mode, when the resulting area is not bigger than what we
  3084.      * started with, extend it to the next block, and then exclude again.
  3085.      */
  3086.     if (!lt(start_pos, old_start) && !lt(old_end, curwin->w_cursor)
  3087.         && VIsual_active)
  3088.     {
  3089.         curwin->w_cursor = old_start;
  3090.         decl(&curwin->w_cursor);
  3091.         if ((pos = findmatch(NULL, what)) == NULL)
  3092.         {
  3093.         curwin->w_cursor = old_pos;
  3094.         return FAIL;
  3095.         }
  3096.         start_pos = *pos;
  3097.         curwin->w_cursor = *pos;
  3098.         if ((end_pos = findmatch(NULL, other)) == NULL)
  3099.         {
  3100.         curwin->w_cursor = old_pos;
  3101.         return FAIL;
  3102.         }
  3103.         curwin->w_cursor = *end_pos;
  3104.     }
  3105.     else
  3106. #endif
  3107.         break;
  3108.     }
  3109.  
  3110. #ifdef FEAT_VISUAL
  3111.     if (VIsual_active)
  3112.     {
  3113.     if (*p_sel == 'e')
  3114.         ++curwin->w_cursor.col;
  3115.     VIsual = start_pos;
  3116.     VIsual_mode = 'v';
  3117.     redraw_curbuf_later(INVERTED);    /* update the inversion */
  3118.     showmode();
  3119.     }
  3120.     else
  3121. #endif
  3122.     {
  3123.     oap->start = start_pos;
  3124.     oap->motion_type = MCHAR;
  3125.     oap->inclusive = TRUE;
  3126.     }
  3127.  
  3128.     return OK;
  3129. }
  3130.  
  3131.     int
  3132. current_par(oap, count, include, type)
  3133.     oparg_T    *oap;
  3134.     long    count;
  3135.     int        include;    /* TRUE == include white space */
  3136.     int        type;        /* 'p' for paragraph, 'S' for section */
  3137. {
  3138.     linenr_T    start_lnum;
  3139.     linenr_T    end_lnum;
  3140.     int        white_in_front;
  3141.     int        dir;
  3142.     int        start_is_white;
  3143.     int        prev_start_is_white;
  3144.     int        retval = OK;
  3145.     int        do_white = FALSE;
  3146.     int        t;
  3147.     int        i;
  3148.  
  3149.     if (type == 'S')        /* not implemented yet */
  3150.     return FAIL;
  3151.  
  3152.     start_lnum = curwin->w_cursor.lnum;
  3153.  
  3154. #ifdef FEAT_VISUAL
  3155.     /*
  3156.      * When visual area is more than one line: extend it.
  3157.      */
  3158.     if (VIsual_active && start_lnum != VIsual.lnum)
  3159.     {
  3160. extend:
  3161.     if (start_lnum < VIsual.lnum)
  3162.         dir = BACKWARD;
  3163.     else
  3164.         dir = FORWARD;
  3165.     for (i = count; --i >= 0; )
  3166.     {
  3167.         if (start_lnum ==
  3168.                (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count))
  3169.         {
  3170.         retval = FAIL;
  3171.         break;
  3172.         }
  3173.  
  3174.         prev_start_is_white = -1;
  3175.         for (t = 0; t < 2; ++t)
  3176.         {
  3177.         start_lnum += dir;
  3178.         start_is_white = linewhite(start_lnum);
  3179.         if (prev_start_is_white == start_is_white)
  3180.         {
  3181.             start_lnum -= dir;
  3182.             break;
  3183.         }
  3184.         for (;;)
  3185.         {
  3186.             if (start_lnum == (dir == BACKWARD
  3187.                         ? 1 : curbuf->b_ml.ml_line_count))
  3188.             break;
  3189.             if (start_is_white != linewhite(start_lnum + dir)
  3190.                 || (!start_is_white
  3191.                     && startPS(start_lnum + (dir > 0
  3192.                                  ? 1 : 0), 0, 0)))
  3193.             break;
  3194.             start_lnum += dir;
  3195.         }
  3196.         if (!include)
  3197.             break;
  3198.         if (start_lnum == (dir == BACKWARD
  3199.                         ? 1 : curbuf->b_ml.ml_line_count))
  3200.             break;
  3201.         prev_start_is_white = start_is_white;
  3202.         }
  3203.     }
  3204.     curwin->w_cursor.lnum = start_lnum;
  3205.     curwin->w_cursor.col = 0;
  3206.     return retval;
  3207.     }
  3208. #endif
  3209.  
  3210.     /*
  3211.      * First move back to the start_lnum of the paragraph or white lines
  3212.      */
  3213.     white_in_front = linewhite(start_lnum);
  3214.     while (start_lnum > 1)
  3215.     {
  3216.     if (white_in_front)        /* stop at first white line */
  3217.     {
  3218.         if (!linewhite(start_lnum - 1))
  3219.         break;
  3220.     }
  3221.     else        /* stop at first non-white line of start of paragraph */
  3222.     {
  3223.         if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0))
  3224.         break;
  3225.     }
  3226.     --start_lnum;
  3227.     }
  3228.  
  3229.     /*
  3230.      * Move past the end of any white lines.
  3231.      */
  3232.     end_lnum = start_lnum;
  3233.     while (linewhite(end_lnum) && end_lnum < curbuf->b_ml.ml_line_count)
  3234.         ++end_lnum;
  3235.  
  3236.     --end_lnum;
  3237.     i = count;
  3238.     if (!include && white_in_front)
  3239.     --i;
  3240.     while (i--)
  3241.     {
  3242.     if (end_lnum == curbuf->b_ml.ml_line_count)
  3243.         return FAIL;
  3244.  
  3245.     if (!include)
  3246.         do_white = linewhite(end_lnum + 1);
  3247.  
  3248.     if (include || !do_white)
  3249.     {
  3250.         ++end_lnum;
  3251.         /*
  3252.          * skip to end of paragraph
  3253.          */
  3254.         while (end_lnum < curbuf->b_ml.ml_line_count
  3255.             && !linewhite(end_lnum + 1)
  3256.             && !startPS(end_lnum + 1, 0, 0))
  3257.         ++end_lnum;
  3258.     }
  3259.  
  3260.     if (i == 0 && white_in_front)
  3261.         break;
  3262.  
  3263.     /*
  3264.      * skip to end of white lines after paragraph
  3265.      */
  3266.     if (include || do_white)
  3267.         while (end_lnum < curbuf->b_ml.ml_line_count
  3268.                            && linewhite(end_lnum + 1))
  3269.         ++end_lnum;
  3270.     }
  3271.  
  3272.     /*
  3273.      * If there are no empty lines at the end, try to find some empty lines at
  3274.      * the start (unless that has been done already).
  3275.      */
  3276.     if (!white_in_front && !linewhite(end_lnum) && include)
  3277.     while (start_lnum > 1 && linewhite(start_lnum - 1))
  3278.         --start_lnum;
  3279.  
  3280. #ifdef FEAT_VISUAL
  3281.     if (VIsual_active)
  3282.     {
  3283.     /* Problem: when doing "Vipipip" nothing happens in a single white
  3284.      * line, we get stuck there.  Trap this here. */
  3285.     if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum)
  3286.         goto extend;
  3287.     VIsual.lnum = start_lnum;
  3288.     VIsual_mode = 'V';
  3289.     redraw_curbuf_later(INVERTED);    /* update the inversion */
  3290.     showmode();
  3291.     }
  3292.     else
  3293. #endif
  3294.     {
  3295.     oap->start.lnum = start_lnum;
  3296.     oap->motion_type = MLINE;
  3297.     }
  3298.     curwin->w_cursor.lnum = end_lnum;
  3299.     curwin->w_cursor.col = 0;
  3300.  
  3301.     return OK;
  3302. }
  3303. #endif
  3304.  
  3305. #if defined(FEAT_LISP) || defined(FEAT_CINDENT) || defined(FEAT_TEXTOBJ) \
  3306.     || defined(PROTO)
  3307. /*
  3308.  * return TRUE if line 'lnum' is empty or has white chars only.
  3309.  */
  3310.     int
  3311. linewhite(lnum)
  3312.     linenr_T    lnum;
  3313. {
  3314.     char_u  *p;
  3315.  
  3316.     p = skipwhite(ml_get(lnum));
  3317.     return (*p == NUL);
  3318. }
  3319. #endif
  3320.  
  3321. #if defined(FEAT_FIND_ID) || defined(PROTO)
  3322. /*
  3323.  * Find identifiers or defines in included files.
  3324.  * if p_ic && (continue_status & CONT_SOL) then ptr must be in lowercase.
  3325.  */
  3326. /*ARGSUSED*/
  3327.     void
  3328. find_pattern_in_path(ptr, dir, len, whole, skip_comments,
  3329.                     type, count, action, start_lnum, end_lnum)
  3330.     char_u    *ptr;        /* pointer to search pattern */
  3331.     int        dir;        /* direction of expansion */
  3332.     int        len;        /* length of search pattern */
  3333.     int        whole;        /* match whole words only */
  3334.     int        skip_comments;    /* don't match inside comments */
  3335.     int        type;        /* Type of search; are we looking for a type?
  3336.                    a macro? */
  3337.     long    count;
  3338.     int        action;        /* What to do when we find it */
  3339.     linenr_T    start_lnum;    /* first line to start searching */
  3340.     linenr_T    end_lnum;    /* last line for searching */
  3341. {
  3342.     SearchedFile *files;        /* Stack of included files */
  3343.     SearchedFile *bigger;        /* When we need more space */
  3344.     int        max_path_depth = 50;
  3345.     long    match_count = 1;
  3346.  
  3347.     char_u    *pat;
  3348.     char_u    *new_fname;
  3349.     char_u    *curr_fname = curbuf->b_fname;
  3350.     char_u    *prev_fname = NULL;
  3351.     linenr_T    lnum;
  3352.     int        depth;
  3353.     int        depth_displayed;    /* For type==CHECK_PATH */
  3354.     int        old_files;
  3355.     int        already_searched;
  3356.     char_u    *file_line;
  3357.     char_u    *line;
  3358.     char_u    *p;
  3359.     char_u    save_char;
  3360.     int        define_matched;
  3361.     regmatch_T    regmatch;
  3362.     regmatch_T    incl_regmatch;
  3363.     regmatch_T    def_regmatch;
  3364.     int        matched = FALSE;
  3365.     int        did_show = FALSE;
  3366.     int        found = FALSE;
  3367.     int        i;
  3368.     char_u    *already = NULL;
  3369.     char_u    *startp = NULL;
  3370. #ifdef RISCOS
  3371.     int        previous_munging = __uname_control;
  3372. #endif
  3373. #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
  3374.     win_T    *curwin_save = NULL;
  3375. #endif
  3376.  
  3377.     regmatch.regprog = NULL;
  3378.     incl_regmatch.regprog = NULL;
  3379.     def_regmatch.regprog = NULL;
  3380.  
  3381.     file_line = alloc(LSIZE);
  3382.     if (file_line == NULL)
  3383.     return;
  3384.  
  3385. #ifdef RISCOS
  3386.     /* UnixLib knows best how to munge c file names - turn munging back on. */
  3387.     __uname_control = __UNAME_LONG_TRUNC;
  3388. #endif
  3389.  
  3390.     if (type != CHECK_PATH && type != FIND_DEFINE
  3391. #ifdef FEAT_INS_EXPAND
  3392.     /* when CONT_SOL is set compare "ptr" with the beginning of the line
  3393.      * is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo */
  3394.         && !(continue_status & CONT_SOL)
  3395. #endif
  3396.        )
  3397.     {
  3398.     pat = alloc(len + 5);
  3399.     if (pat == NULL)
  3400.         goto fpip_end;
  3401.     sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", len, ptr);
  3402.     /* ignore case according to p_ic, p_scs and pat */
  3403.     regmatch.rm_ic = ignorecase(pat);
  3404.     regmatch.regprog = vim_regcomp(pat, (int)p_magic);
  3405.     vim_free(pat);
  3406.     if (regmatch.regprog == NULL)
  3407.         goto fpip_end;
  3408.     }
  3409.     if (*curbuf->b_p_inc != NUL || *p_inc != NUL)
  3410.     {
  3411.     incl_regmatch.regprog = vim_regcomp(*curbuf->b_p_inc == NUL
  3412.                      ? p_inc : curbuf->b_p_inc, (int)p_magic);
  3413.     if (incl_regmatch.regprog == NULL)
  3414.         goto fpip_end;
  3415.     incl_regmatch.rm_ic = FALSE;    /* don't ignore case in incl. pat. */
  3416.     }
  3417.     if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL))
  3418.     {
  3419.     def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL
  3420.                      ? p_def : curbuf->b_p_def, (int)p_magic);
  3421.     if (def_regmatch.regprog == NULL)
  3422.         goto fpip_end;
  3423.     def_regmatch.rm_ic = FALSE;    /* don't ignore case in define pat. */
  3424.     }
  3425.     files = (SearchedFile *)lalloc((long_u)
  3426.                    (max_path_depth * sizeof(SearchedFile)), TRUE);
  3427.     if (files == NULL)
  3428.     goto fpip_end;
  3429.     for (i = 0; i < max_path_depth; i++)
  3430.     {
  3431.     files[i].fp = NULL;
  3432.     files[i].name = NULL;
  3433.     files[i].lnum = 0;
  3434.     files[i].matched = FALSE;
  3435.     }
  3436.     old_files = max_path_depth;
  3437.     depth = depth_displayed = -1;
  3438.  
  3439.     lnum = start_lnum;
  3440.     if (end_lnum > curbuf->b_ml.ml_line_count)
  3441.     end_lnum = curbuf->b_ml.ml_line_count;
  3442.     if (lnum > end_lnum)        /* do at least one line */
  3443.     lnum = end_lnum;
  3444.     line = ml_get(lnum);
  3445.  
  3446.     for (;;)
  3447.     {
  3448.     if (incl_regmatch.regprog != NULL
  3449.         && vim_regexec(&incl_regmatch, line, (colnr_T)0))
  3450.     {
  3451.         new_fname = file_name_in_line(incl_regmatch.endp[0],
  3452.             0, FNAME_EXP|FNAME_INCL|FNAME_REL, 1L,
  3453.             curr_fname == curbuf->b_fname
  3454.                          ? curbuf->b_ffname : curr_fname);
  3455.         already_searched = FALSE;
  3456.         if (new_fname != NULL)
  3457.         {
  3458.         /* Check whether we have already searched in this file */
  3459.         for (i = 0;; i++)
  3460.         {
  3461.             if (i == depth + 1)
  3462.             i = old_files;
  3463.             if (i == max_path_depth)
  3464.             break;
  3465.             if (STRCMP(new_fname, files[i].name) == 0)
  3466.             {
  3467.             if (type != CHECK_PATH &&
  3468.                 action == ACTION_SHOW_ALL && files[i].matched)
  3469.             {
  3470.                 msg_putchar('\n');        /* cursor below last one */
  3471.                 if (!got_int)        /* don't display if 'q'
  3472.                                typed at "--more--"
  3473.                                mesage */
  3474.                 {
  3475.                 msg_home_replace_hl(new_fname);
  3476.                 MSG_PUTS(_(" (includes previously listed match)"));
  3477.                 prev_fname = NULL;
  3478.                 }
  3479.             }
  3480.             vim_free(new_fname);
  3481.             new_fname = NULL;
  3482.             already_searched = TRUE;
  3483.             break;
  3484.             }
  3485.         }
  3486.         }
  3487.  
  3488.         if (type == CHECK_PATH && (action == ACTION_SHOW_ALL
  3489.                  || (new_fname == NULL && !already_searched)))
  3490.         {
  3491.         if (did_show)
  3492.             msg_putchar('\n');        /* cursor below last one */
  3493.         else
  3494.         {
  3495.             gotocmdline(TRUE);        /* cursor at status line */
  3496.             MSG_PUTS_TITLE(_("--- Included files "));
  3497.             if (action != ACTION_SHOW_ALL)
  3498.             MSG_PUTS_TITLE(_("not found "));
  3499.             MSG_PUTS_TITLE(_("in path ---\n"));
  3500.         }
  3501.         did_show = TRUE;
  3502.         while (depth_displayed < depth && !got_int)
  3503.         {
  3504.             ++depth_displayed;
  3505.             for (i = 0; i < depth_displayed; i++)
  3506.             MSG_PUTS("  ");
  3507.             msg_home_replace(files[depth_displayed].name);
  3508.             MSG_PUTS(" -->\n");
  3509.         }
  3510.         if (!got_int)            /* don't display if 'q' typed
  3511.                            for "--more--" message */
  3512.         {
  3513.             for (i = 0; i <= depth_displayed; i++)
  3514.             MSG_PUTS("  ");
  3515.             /*
  3516.              * Isolate the file name.
  3517.              * Include the surrounding "" or <> if present.
  3518.              */
  3519.             for (p = incl_regmatch.endp[0] + 1; !vim_isfilec(*p); p++)
  3520.             ;
  3521.             for (i = 0; vim_isfilec(p[i]); i++)
  3522.             ;
  3523.             if (p[-1] == '"' || p[-1] == '<')
  3524.             {
  3525.             --p;
  3526.             ++i;
  3527.             }
  3528.             if (p[i] == '"' || p[i] == '>')
  3529.             ++i;
  3530.             save_char = p[i];
  3531.             p[i] = NUL;
  3532.                 /* Same highlighting as for directories */
  3533.             msg_outtrans_attr(p, hl_attr(HLF_D));
  3534.             p[i] = save_char;
  3535.             if (new_fname == NULL && action == ACTION_SHOW_ALL)
  3536.             {
  3537.             if (already_searched)
  3538.                 MSG_PUTS(_("  (Already listed)"));
  3539.             else
  3540.                 MSG_PUTS(_("  NOT FOUND"));
  3541.             }
  3542.         }
  3543.         out_flush();        /* output each line directly */
  3544.         }
  3545.  
  3546.         if (new_fname != NULL)
  3547.         {
  3548.         /* Push the new file onto the file stack */
  3549.         if (depth + 1 == old_files)
  3550.         {
  3551.             bigger = (SearchedFile *)lalloc((long_u)(
  3552.                 max_path_depth * 2 * sizeof(SearchedFile)), TRUE);
  3553.             if (bigger != NULL)
  3554.             {
  3555.             for (i = 0; i <= depth; i++)
  3556.                 bigger[i] = files[i];
  3557.             for (i = depth + 1; i < old_files + max_path_depth; i++)
  3558.             {
  3559.                 bigger[i].fp = NULL;
  3560.                 bigger[i].name = NULL;
  3561.                 bigger[i].lnum = 0;
  3562.                 bigger[i].matched = FALSE;
  3563.             }
  3564.             for (i = old_files; i < max_path_depth; i++)
  3565.                 bigger[i + max_path_depth] = files[i];
  3566.             old_files += max_path_depth;
  3567.             max_path_depth *= 2;
  3568.             vim_free(files);
  3569.             files = bigger;
  3570.             }
  3571.         }
  3572.         if ((files[depth + 1].fp = mch_fopen((char *)new_fname, "r"))
  3573.                                     == NULL)
  3574.             vim_free(new_fname);
  3575.         else
  3576.         {
  3577.             if (++depth == old_files)
  3578.             {
  3579.             /*
  3580.              * lalloc() for 'bigger' must have failed above.  We
  3581.              * will forget one of our already visited files now.
  3582.              */
  3583.             vim_free(files[old_files].name);
  3584.             ++old_files;
  3585.             }
  3586.             files[depth].name = curr_fname = new_fname;
  3587.             files[depth].lnum = 0;
  3588.             files[depth].matched = FALSE;
  3589. #ifdef FEAT_INS_EXPAND
  3590.             if (action == ACTION_EXPAND)
  3591.             {
  3592.             sprintf((char*)IObuff, _("Scanning included file: %s"),
  3593.                 (char *)new_fname);
  3594.             msg_trunc_attr(IObuff, TRUE, hl_attr(HLF_R));
  3595.             }
  3596. #endif
  3597.         }
  3598.         }
  3599.     }
  3600.     else
  3601.     {
  3602.         /*
  3603.          * Check if the line is a define (type == FIND_DEFINE)
  3604.          */
  3605.         p = line;
  3606. search_line:
  3607.         define_matched = FALSE;
  3608.         if (def_regmatch.regprog != NULL
  3609.                   && vim_regexec(&def_regmatch, line, (colnr_T)0))
  3610.         {
  3611.         /*
  3612.          * Pattern must be first identifier after 'define', so skip
  3613.          * to that position before checking for match of pattern.  Also
  3614.          * don't let it match beyond the end of this identifier.
  3615.          */
  3616.         p = def_regmatch.endp[0];
  3617.         while (*p && !vim_isIDc(*p))
  3618.             p++;
  3619.         define_matched = TRUE;
  3620.         }
  3621.  
  3622.         /*
  3623.          * Look for a match.  Don't do this if we are looking for a
  3624.          * define and this line didn't match define_prog above.
  3625.          */
  3626.         if (def_regmatch.regprog == NULL || define_matched)
  3627.         {
  3628.         if (define_matched
  3629. #ifdef FEAT_INS_EXPAND
  3630.             || (continue_status & CONT_SOL)
  3631. #endif
  3632.             )
  3633.         {
  3634.             /* compare the first "len" chars from "ptr" */
  3635.             startp = skipwhite(p);
  3636.             if (p_ic)
  3637.             matched = !MB_STRNICMP(startp, ptr, len);
  3638.             else
  3639.             matched = !STRNCMP(startp, ptr, len);
  3640.             if (matched && define_matched && whole
  3641.                             && vim_isIDc(startp[len]))
  3642.             matched = FALSE;
  3643.         }
  3644.         else if (regmatch.regprog != NULL
  3645.              && vim_regexec(®match, line, (colnr_T)(p - line)))
  3646.         {
  3647.             matched = TRUE;
  3648.             startp = regmatch.startp[0];
  3649.             /*
  3650.              * Check if the line is not a comment line (unless we are
  3651.              * looking for a define).  A line starting with "# define"
  3652.              * is not considered to be a comment line.
  3653.              */
  3654.             if (!define_matched && skip_comments)
  3655.             {
  3656. #ifdef FEAT_COMMENTS
  3657.             if ((*line != '#' ||
  3658.                 STRNCMP(skipwhite(line + 1), "define", 6) != 0)
  3659.                 && get_leader_len(line, NULL, FALSE))
  3660.                 matched = FALSE;
  3661.  
  3662.             /*
  3663.              * Also check for a "/ *" or "/ /" before the match.
  3664.              * Skips lines like "int backwards;  / * normal index
  3665.              * * /" when looking for "normal".
  3666.              * Note: Doesn't skip "/ *" in comments.
  3667.              */
  3668.             p = skipwhite(line);
  3669.             if (matched
  3670.                 || (p[0] == '/' && p[1] == '*') || p[0] == '*')
  3671. #endif
  3672.                 for (p = line; *p && p < startp; ++p)
  3673.                 {
  3674.                 if (matched
  3675.                     && p[0] == '/'
  3676.                     && (p[1] == '*' || p[1] == '/'))
  3677.                 {
  3678.                     matched = FALSE;
  3679.                     /* After "//" all text is comment */
  3680.                     if (p[1] == '/')
  3681.                     break;
  3682.                     ++p;
  3683.                 }
  3684.                 else if (!matched && p[0] == '*' && p[1] == '/')
  3685.                 {
  3686.                     /* Can find match after "* /". */
  3687.                     matched = TRUE;
  3688.                     ++p;
  3689.                 }
  3690.                 }
  3691.             }
  3692.         }
  3693.         }
  3694.     }
  3695.     if (matched)
  3696.     {
  3697. #ifdef FEAT_INS_EXPAND
  3698.         if (action == ACTION_EXPAND)
  3699.         {
  3700.         int    reuse = 0;
  3701.         int    add_r;
  3702.         char_u    *aux;
  3703.  
  3704.         if (depth == -1 && lnum == curwin->w_cursor.lnum)
  3705.             break;
  3706.         found = TRUE;
  3707.         aux = p = startp;
  3708.         if (continue_status & CONT_ADDING)
  3709.         {
  3710.             p += completion_length;
  3711.             if (vim_iswordp(p))
  3712.             goto exit_matched;
  3713.             p = find_word_start(p);
  3714.         }
  3715.         p = find_word_end(p);
  3716.         i = (int)(p - aux);
  3717.  
  3718.         if ((continue_status & CONT_ADDING) && i == completion_length)
  3719.         {
  3720.             /* get the next line */
  3721.             /* IOSIZE > completion_length, so the STRNCPY works */
  3722.             STRNCPY(IObuff, aux, i);
  3723.             if (!(     depth < 0
  3724.                 && lnum < end_lnum
  3725.                 && (line = ml_get(++lnum)) != NULL)
  3726.             && !(    depth >= 0
  3727.                 && !vim_fgets(line = file_line,
  3728.                              LSIZE, files[depth].fp)))
  3729.             goto exit_matched;
  3730.  
  3731.             /* we read a line, set "already" to check this "line" later
  3732.              * if depth >= 0 we'll increase files[depth].lnum far
  3733.              * bellow  -- Acevedo */
  3734.             already = aux = p = skipwhite(line);
  3735.             p = find_word_start(p);
  3736.             p = find_word_end(p);
  3737.             if (p > aux)
  3738.             {
  3739.             if (*aux != ')' && IObuff[i-1] != TAB)
  3740.             {
  3741.                 if (IObuff[i-1] != ' ')
  3742.                 IObuff[i++] = ' ';
  3743.                 /* IObuf =~ "\(\k\|\i\).* ", thus i >= 2*/
  3744.                 if (p_js
  3745.                 && (IObuff[i-2] == '.'
  3746.                     || (vim_strchr(p_cpo, CPO_JOINSP) == NULL
  3747.                     && (IObuff[i-2] == '?'
  3748.                         || IObuff[i-2] == '!'))))
  3749.                 IObuff[i++] = ' ';
  3750.             }
  3751.             /* copy as much as posible of the new word */
  3752.             if (p - aux >= IOSIZE - i)
  3753.                 p = aux + IOSIZE - i - 1;
  3754.             STRNCPY(IObuff + i, aux, p - aux);
  3755.             i += (int)(p - aux);
  3756.             reuse |= CONT_S_IPOS;
  3757.             }
  3758.             IObuff[i] = NUL;
  3759.             aux = IObuff;
  3760.  
  3761.             if (i == completion_length)
  3762.             goto exit_matched;
  3763.         }
  3764.  
  3765.         add_r = ins_compl_add_infercase(aux, i,
  3766.             curr_fname == curbuf->b_fname ? NULL : curr_fname,
  3767.             dir, reuse);
  3768.         if (add_r == OK)
  3769.             /* if dir was BACKWARD then honor it just once */
  3770.             dir = FORWARD;
  3771.         else if (add_r == RET_ERROR)
  3772.             break;
  3773.         }
  3774.         else
  3775. #endif
  3776.          if (action == ACTION_SHOW_ALL)
  3777.         {
  3778.         found = TRUE;
  3779.         if (!did_show)
  3780.             gotocmdline(TRUE);        /* cursor at status line */
  3781.         if (curr_fname != prev_fname)
  3782.         {
  3783.             if (did_show)
  3784.             msg_putchar('\n');    /* cursor below last one */
  3785.             if (!got_int)        /* don't display if 'q' typed
  3786.                             at "--more--" mesage */
  3787.             msg_home_replace_hl(curr_fname);
  3788.             prev_fname = curr_fname;
  3789.         }
  3790.         did_show = TRUE;
  3791.         if (!got_int)
  3792.             show_pat_in_path(line, type, TRUE, action,
  3793.                 (depth == -1) ? NULL : files[depth].fp,
  3794.                 (depth == -1) ? &lnum : &files[depth].lnum,
  3795.                 match_count++);
  3796.  
  3797.         /* Set matched flag for this file and all the ones that
  3798.          * include it */
  3799.         for (i = 0; i <= depth; ++i)
  3800.             files[i].matched = TRUE;
  3801.         }
  3802.         else if (--count <= 0)
  3803.         {
  3804.         found = TRUE;
  3805.         if (depth == -1 && lnum == curwin->w_cursor.lnum
  3806. #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
  3807.                               && g_do_tagpreview == 0
  3808. #endif
  3809.                               )
  3810.             EMSG(_("E387: Match is on current line"));
  3811.         else if (action == ACTION_SHOW)
  3812.         {
  3813.             show_pat_in_path(line, type, did_show, action,
  3814.             (depth == -1) ? NULL : files[depth].fp,
  3815.             (depth == -1) ? &lnum : &files[depth].lnum, 1L);
  3816.             did_show = TRUE;
  3817.         }
  3818.         else
  3819.         {
  3820. #ifdef FEAT_GUI
  3821.             need_mouse_correct = TRUE;
  3822. #endif
  3823. #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
  3824.             /* ":psearch" uses the preview window */
  3825.             if (g_do_tagpreview != 0)
  3826.             {
  3827.             curwin_save = curwin;
  3828.             prepare_tagpreview();
  3829.             }
  3830. #endif
  3831.             if (action == ACTION_SPLIT)
  3832.             {
  3833. #ifdef FEAT_WINDOWS
  3834.             if (win_split(0, 0) == FAIL)
  3835. #endif
  3836.                 break;
  3837.             }
  3838.             if (depth == -1)
  3839.             {
  3840.             /* match in current file */
  3841. #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
  3842.             if (g_do_tagpreview != 0)
  3843.             {
  3844.                 if (getfile(0, curwin_save->w_buffer->b_fname,
  3845.                          NULL, TRUE, lnum, FALSE) > 0)
  3846.                 break;    /* failed to jump to file */
  3847.             }
  3848.             else
  3849. #endif
  3850.                 setpcmark();
  3851.             curwin->w_cursor.lnum = lnum;
  3852.             }
  3853.             else
  3854.             {
  3855.             if (getfile(0, files[depth].name, NULL, TRUE,
  3856.                         files[depth].lnum, FALSE) > 0)
  3857.                 break;    /* failed to jump to file */
  3858.             /* autocommands may have changed the lnum, we don't
  3859.              * want that here */
  3860.             curwin->w_cursor.lnum = files[depth].lnum;
  3861.             }
  3862.         }
  3863.         if (action != ACTION_SHOW)
  3864.         {
  3865.             curwin->w_cursor.col = (colnr_T) (startp - line);
  3866.             curwin->w_set_curswant = TRUE;
  3867.         }
  3868.  
  3869. #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
  3870.         if (g_do_tagpreview != 0
  3871.             && curwin != curwin_save && win_valid(curwin_save))
  3872.         {
  3873.             /* Return cursor to where we were */
  3874.             validate_cursor();
  3875.             redraw_later(VALID);
  3876.             win_enter(curwin_save, TRUE);
  3877.         }
  3878. #endif
  3879.         break;
  3880.         }
  3881. #ifdef FEAT_INS_EXPAND
  3882. exit_matched:
  3883. #endif
  3884.         matched = FALSE;
  3885.         /* look for other matches in the rest of the line if we
  3886.          * are not at the end of it already */
  3887.         if (def_regmatch.regprog == NULL
  3888. #ifdef FEAT_INS_EXPAND
  3889.             && action == ACTION_EXPAND
  3890.             && !(continue_status & CONT_SOL)
  3891. #endif
  3892.             && *(p = startp + 1))
  3893.         goto search_line;
  3894.     }
  3895.     line_breakcheck();
  3896. #ifdef FEAT_INS_EXPAND
  3897.     if (action == ACTION_EXPAND)
  3898.         ins_compl_check_keys();
  3899.     if (got_int || completion_interrupted)
  3900. #else
  3901.     if (got_int)
  3902. #endif
  3903.         break;
  3904.  
  3905.     /*
  3906.      * Read the next line.  When reading an included file and encountering
  3907.      * end-of-file, close the file and continue in the file that included
  3908.      * it.
  3909.      */
  3910.     while (depth >= 0 && !already
  3911.         && vim_fgets(line = file_line, LSIZE, files[depth].fp))
  3912.     {
  3913.         fclose(files[depth].fp);
  3914.         --old_files;
  3915.         files[old_files].name = files[depth].name;
  3916.         files[old_files].matched = files[depth].matched;
  3917.         --depth;
  3918.         curr_fname = (depth == -1) ? curbuf->b_fname
  3919.                        : files[depth].name;
  3920.         if (depth < depth_displayed)
  3921.         depth_displayed = depth;
  3922.     }
  3923.     if (depth >= 0)        /* we could read the line */
  3924.         files[depth].lnum++;
  3925.     else if (!already)
  3926.     {
  3927.         if (++lnum > end_lnum)
  3928.         break;
  3929.         line = ml_get(lnum);
  3930.     }
  3931.     already = NULL;
  3932.     }
  3933.     /* End of big for (;;) loop. */
  3934.  
  3935.     /* Close any files that are still open. */
  3936.     for (i = 0; i <= depth; i++)
  3937.     {
  3938.     fclose(files[i].fp);
  3939.     vim_free(files[i].name);
  3940.     }
  3941.     for (i = old_files; i < max_path_depth; i++)
  3942.     vim_free(files[i].name);
  3943.     vim_free(files);
  3944.  
  3945.     if (type == CHECK_PATH)
  3946.     {
  3947.     if (!did_show)
  3948.     {
  3949.         if (action != ACTION_SHOW_ALL)
  3950.         MSG(_("All included files were found"));
  3951.         else
  3952.         MSG(_("No included files"));
  3953.     }
  3954.     }
  3955.     else if (!found
  3956. #ifdef FEAT_INS_EXPAND
  3957.             && action != ACTION_EXPAND
  3958. #endif
  3959.                         )
  3960.     {
  3961. #ifdef FEAT_INS_EXPAND
  3962.     if (got_int || completion_interrupted)
  3963. #else
  3964.     if (got_int)
  3965. #endif
  3966.         EMSG(_(e_interr));
  3967.     else if (type == FIND_DEFINE)
  3968.         EMSG(_("E388: Couldn't find definition"));
  3969.     else
  3970.         EMSG(_("E389: Couldn't find pattern"));
  3971.     }
  3972.     if (action == ACTION_SHOW || action == ACTION_SHOW_ALL)
  3973.     msg_end();
  3974.  
  3975. fpip_end:
  3976.     vim_free(file_line);
  3977.     vim_free(regmatch.regprog);
  3978.     vim_free(incl_regmatch.regprog);
  3979.     vim_free(def_regmatch.regprog);
  3980.  
  3981. #ifdef RISCOS
  3982.    /* Restore previous file munging state. */
  3983.     __uname_control = previous_munging;
  3984. #endif
  3985. }
  3986.  
  3987.     static void
  3988. show_pat_in_path(line, type, did_show, action, fp, lnum, count)
  3989.     char_u  *line;
  3990.     int        type;
  3991.     int        did_show;
  3992.     int        action;
  3993.     FILE    *fp;
  3994.     linenr_T *lnum;
  3995.     long    count;
  3996. {
  3997.     char_u  *p;
  3998.  
  3999.     if (did_show)
  4000.     msg_putchar('\n');    /* cursor below last one */
  4001.     else
  4002.     gotocmdline(TRUE);    /* cursor at status line */
  4003.     if (got_int)        /* 'q' typed at "--more--" message */
  4004.     return;
  4005.     for (;;)
  4006.     {
  4007.     p = line + STRLEN(line) - 1;
  4008.     if (fp != NULL)
  4009.     {
  4010.         /* We used fgets(), so get rid of newline at end */
  4011.         if (p >= line && *p == '\n')
  4012.         --p;
  4013.         if (p >= line && *p == '\r')
  4014.         --p;
  4015.         *(p + 1) = NUL;
  4016.     }
  4017.     if (action == ACTION_SHOW_ALL)
  4018.     {
  4019.         sprintf((char *)IObuff, "%3ld: ", count);    /* show match nr */
  4020.         msg_puts(IObuff);
  4021.         sprintf((char *)IObuff, "%4ld", *lnum);    /* show line nr */
  4022.                         /* Highlight line numbers */
  4023.         msg_puts_attr(IObuff, hl_attr(HLF_N));
  4024.         MSG_PUTS(" ");
  4025.     }
  4026.     msg_prt_line(line);
  4027.     out_flush();            /* show one line at a time */
  4028.  
  4029.     /* Definition continues until line that doesn't end with '\' */
  4030.     if (got_int || type != FIND_DEFINE || p < line || *p != '\\')
  4031.         break;
  4032.  
  4033.     if (fp != NULL)
  4034.     {
  4035.         if (vim_fgets(line, LSIZE, fp)) /* end of file */
  4036.         break;
  4037.         ++*lnum;
  4038.     }
  4039.     else
  4040.     {
  4041.         if (++*lnum > curbuf->b_ml.ml_line_count)
  4042.         break;
  4043.         line = ml_get(*lnum);
  4044.     }
  4045.     msg_putchar('\n');
  4046.     }
  4047. }
  4048. #endif
  4049.  
  4050. #ifdef FEAT_VIMINFO
  4051.     int
  4052. read_viminfo_search_pattern(virp, force)
  4053.     vir_T    *virp;
  4054.     int        force;
  4055. {
  4056.     char_u    *lp;
  4057.     int        idx = -1;
  4058.     int        magic = FALSE;
  4059.     int        no_scs = FALSE;
  4060.     int        off_line = FALSE;
  4061.     int        off_end = FALSE;
  4062.     long    off = 0;
  4063.     int        setlast = FALSE;
  4064. #ifdef FEAT_SEARCH_EXTRA
  4065.     static int    hlsearch_on = FALSE;
  4066. #endif
  4067.     char_u    *val;
  4068.  
  4069.     /*
  4070.      * Old line types:
  4071.      * "/pat", "&pat": search/subst. pat
  4072.      * "~/pat", "~&pat": last used search/subst. pat
  4073.      * New line types:
  4074.      * "~h", "~H": hlsearch highlighting off/on
  4075.      * "~<magic><smartcase><line><end><off><last><which>pat"
  4076.      * <magic>: 'm' off, 'M' on
  4077.      * <smartcase>: 's' off, 'S' on
  4078.      * <line>: 'L' line offset, 'l' char offset
  4079.      * <end>: 'E' from end, 'e' from start
  4080.      * <off>: decimal, offset
  4081.      * <last>: '~' last used pattern
  4082.      * <which>: '/' search pat, '&' subst. pat
  4083.      */
  4084.     lp = virp->vir_line;
  4085.     if (lp[0] == '~' && (lp[1] == 'm' || lp[1] == 'M'))    /* new line type */
  4086.     {
  4087.     if (lp[1] == 'M')        /* magic on */
  4088.         magic = TRUE;
  4089.     if (lp[2] == 's')
  4090.         no_scs = TRUE;
  4091.     if (lp[3] == 'L')
  4092.         off_line = TRUE;
  4093.     if (lp[4] == 'E')
  4094.         off_end = TRUE;
  4095.     lp += 5;
  4096.     off = getdigits(&lp);
  4097.     }
  4098.     if (lp[0] == '~')        /* use this pattern for last-used pattern */
  4099.     {
  4100.     setlast = TRUE;
  4101.     lp++;
  4102.     }
  4103.     if (lp[0] == '/')
  4104.     idx = RE_SEARCH;
  4105.     else if (lp[0] == '&')
  4106.     idx = RE_SUBST;
  4107. #ifdef FEAT_SEARCH_EXTRA
  4108.     else if (lp[0] == 'h')    /* ~h: 'hlsearch' highlighting off */
  4109.     hlsearch_on = FALSE;
  4110.     else if (lp[0] == 'H')    /* ~H: 'hlsearch' highlighting on */
  4111.     hlsearch_on = TRUE;
  4112. #endif
  4113.     if (idx >= 0)
  4114.     {
  4115.     if (force || spats[idx].pat == NULL)
  4116.     {
  4117.         val = viminfo_readstring(virp, (int)(lp - virp->vir_line + 1),
  4118.                                     TRUE);
  4119.         if (val != NULL)
  4120.         {
  4121.         set_last_search_pat(val, idx, magic, setlast);
  4122.         vim_free(val);
  4123.         spats[idx].no_scs = no_scs;
  4124.         spats[idx].off.line = off_line;
  4125.         spats[idx].off.end = off_end;
  4126.         spats[idx].off.off = off;
  4127. #ifdef FEAT_SEARCH_EXTRA
  4128.         if (setlast)
  4129.             no_hlsearch = !hlsearch_on;
  4130. #endif
  4131.         }
  4132.     }
  4133.     }
  4134.     return viminfo_readline(virp);
  4135. }
  4136.  
  4137.     void
  4138. write_viminfo_search_pattern(fp)
  4139.     FILE    *fp;
  4140. {
  4141.     if (get_viminfo_parameter('/') != 0)
  4142.     {
  4143. #ifdef FEAT_SEARCH_EXTRA
  4144.     fprintf(fp, "\n# hlsearch on (H) or off (h):\n~%c",
  4145.         (no_hlsearch || find_viminfo_parameter('h') != NULL) ? 'h' : 'H');
  4146. #endif
  4147.     wvsp_one(fp, RE_SEARCH, "", '/');
  4148.     wvsp_one(fp, RE_SUBST, "Substitute ", '&');
  4149.     }
  4150. }
  4151.  
  4152.     static void
  4153. wvsp_one(fp, idx, s, sc)
  4154.     FILE    *fp;    /* file to write to */
  4155.     int        idx;    /* spats[] index */
  4156.     char    *s;    /* search pat */
  4157.     int        sc;    /* dir char */
  4158. {
  4159.     if (spats[idx].pat != NULL)
  4160.     {
  4161.     fprintf(fp, "\n# Last %sSearch Pattern:\n~", s);
  4162.     /* off.dir is not stored, it's reset to forward */
  4163.     fprintf(fp, "%c%c%c%c%ld%s%c",
  4164.         spats[idx].magic    ? 'M' : 'm',    /* magic */
  4165.         spats[idx].no_scs   ? 's' : 'S',    /* smartcase */
  4166.         spats[idx].off.line ? 'L' : 'l',    /* line offset */
  4167.         spats[idx].off.end  ? 'E' : 'e',    /* offset from end */
  4168.         spats[idx].off.off,            /* offset */
  4169.         last_idx == idx        ? "~" : "",        /* last used pat */
  4170.         sc);
  4171.     viminfo_writestring(fp, spats[idx].pat);
  4172.     }
  4173. }
  4174. #endif /* FEAT_VIMINFO */
  4175.